2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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"
34 #include "SecItemPriv.h"
35 #include "SecIdentitySearchPriv.h"
36 #include "SecKeychainPriv.h"
37 #include "SecCertificatePriv.h"
38 #include "SecCertificatePrivP.h"
39 #include "TrustAdditions.h"
40 #include "TrustSettingsSchema.h"
41 #include <Security/SecTrustPriv.h>
42 #include "utilities/array_size.h"
44 #include <AssertMacros.h>
48 #include <Security/SecTrustedApplication.h>
49 #include <Security/SecTrustedApplicationPriv.h>
50 #include <Security/SecCode.h>
51 #include <Security/SecCodePriv.h>
52 #include <Security/SecRequirement.h>
54 #include <login/SessionAgentCom.h>
55 #include <login/SessionAgentStatusCom.h>
58 const uint8_t kUUIDStringLength
= 36;
60 OSStatus
SecItemAdd_osx(CFDictionaryRef attributes
, CFTypeRef
*result
);
61 OSStatus
SecItemCopyMatching_osx(CFDictionaryRef query
, CFTypeRef
*result
);
62 OSStatus
SecItemUpdate_osx(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
63 OSStatus
SecItemDelete_osx(CFDictionaryRef query
);
66 OSStatus
SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
);
67 OSStatus
SecItemCopyMatching_ios(CFDictionaryRef query
, CFTypeRef
*result
);
68 OSStatus
SecItemUpdate_ios(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
69 OSStatus
SecItemDelete_ios(CFDictionaryRef query
);
70 OSStatus
SecItemUpdateTokenItems_ios(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
);
73 OSStatus
SecItemValidateAppleApplicationGroupAccess(CFStringRef group
);
74 CFDictionaryRef
SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
75 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
);
78 static Boolean
SecItemSynchronizable(CFDictionaryRef query
);
80 static void secitemlog(int priority
, const char *format
, ...)
85 if (priority
< LOG_NOTICE
) // log warnings and errors
89 va_start(list
, format
);
90 vsyslog(priority
, format
, list
);
95 static void secitemshow(CFTypeRef obj
, const char *context
)
98 CFStringRef desc
= CFCopyDescription(obj
);
101 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc
), kCFStringEncodingUTF8
) + 1;
102 char* buffer
= (char*) malloc(length
);
104 Boolean converted
= CFStringGetCString(desc
, buffer
, length
, kCFStringEncodingUTF8
);
106 const char *prefix
= (context
) ? context
: "";
107 const char *separator
= (context
) ? " " : "";
108 secitemlog(LOG_NOTICE
, "%s%s%s", prefix
, separator
, buffer
);
117 #define CFDataGetBytePtrVoid CFDataGetBytePtr
119 #pragma mark SecItem private utility functions
121 /******************************************************************************/
123 struct ProtocolAttributeInfo
{
124 const CFStringRef
*protocolValue
;
125 SecProtocolType protocolType
;
128 static ProtocolAttributeInfo gProtocolTypes
[] = {
129 { &kSecAttrProtocolFTP
, kSecProtocolTypeFTP
},
130 { &kSecAttrProtocolFTPAccount
, kSecProtocolTypeFTPAccount
},
131 { &kSecAttrProtocolHTTP
, kSecProtocolTypeHTTP
},
132 { &kSecAttrProtocolIRC
, kSecProtocolTypeIRC
},
133 { &kSecAttrProtocolNNTP
, kSecProtocolTypeNNTP
},
134 { &kSecAttrProtocolPOP3
, kSecProtocolTypePOP3
},
135 { &kSecAttrProtocolSMTP
, kSecProtocolTypeSMTP
},
136 { &kSecAttrProtocolSOCKS
, kSecProtocolTypeSOCKS
},
137 { &kSecAttrProtocolIMAP
, kSecProtocolTypeIMAP
},
138 { &kSecAttrProtocolLDAP
, kSecProtocolTypeLDAP
},
139 { &kSecAttrProtocolAppleTalk
, kSecProtocolTypeAppleTalk
},
140 { &kSecAttrProtocolAFP
, kSecProtocolTypeAFP
},
141 { &kSecAttrProtocolTelnet
, kSecProtocolTypeTelnet
},
142 { &kSecAttrProtocolSSH
, kSecProtocolTypeSSH
},
143 { &kSecAttrProtocolFTPS
, kSecProtocolTypeFTPS
},
144 { &kSecAttrProtocolHTTPS
, kSecProtocolTypeHTTPS
},
145 { &kSecAttrProtocolHTTPProxy
, kSecProtocolTypeHTTPProxy
},
146 { &kSecAttrProtocolHTTPSProxy
, kSecProtocolTypeHTTPSProxy
},
147 { &kSecAttrProtocolFTPProxy
, kSecProtocolTypeFTPProxy
},
148 { &kSecAttrProtocolSMB
, kSecProtocolTypeSMB
},
149 { &kSecAttrProtocolRTSP
, kSecProtocolTypeRTSP
},
150 { &kSecAttrProtocolRTSPProxy
, kSecProtocolTypeRTSPProxy
},
151 { &kSecAttrProtocolDAAP
, kSecProtocolTypeDAAP
},
152 { &kSecAttrProtocolEPPC
, kSecProtocolTypeEPPC
},
153 { &kSecAttrProtocolIPP
, kSecProtocolTypeIPP
},
154 { &kSecAttrProtocolNNTPS
, kSecProtocolTypeNNTPS
},
155 { &kSecAttrProtocolLDAPS
, kSecProtocolTypeLDAPS
},
156 { &kSecAttrProtocolTelnetS
, kSecProtocolTypeTelnetS
},
157 { &kSecAttrProtocolIMAPS
, kSecProtocolTypeIMAPS
},
158 { &kSecAttrProtocolIRCS
, kSecProtocolTypeIRCS
},
159 { &kSecAttrProtocolPOP3S
, kSecProtocolTypePOP3S
}
162 static const int kNumberOfProtocolTypes
= sizeof(gProtocolTypes
) / sizeof(ProtocolAttributeInfo
);
165 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
167 static SecProtocolType
168 _SecProtocolTypeForSecAttrProtocol(
171 SecProtocolType result
= kSecProtocolTypeAny
;
173 if (protocol
!= NULL
) {
175 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
176 if (CFEqual(protocol
, *(gProtocolTypes
[count
].protocolValue
))) {
177 result
= gProtocolTypes
[count
].protocolType
;
187 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
190 _SecAttrProtocolForSecProtocolType(
191 SecProtocolType protocolType
)
193 CFTypeRef result
= NULL
;
195 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
196 if (gProtocolTypes
[count
].protocolType
== protocolType
) {
197 result
= *(gProtocolTypes
[count
].protocolValue
);
206 /******************************************************************************/
208 struct AuthenticationAttributeInfo
{
209 const CFStringRef
*authValue
;
210 SecAuthenticationType authType
;
213 static AuthenticationAttributeInfo gAuthTypes
[] = {
214 { &kSecAttrAuthenticationTypeNTLM
, kSecAuthenticationTypeNTLM
},
215 { &kSecAttrAuthenticationTypeMSN
, kSecAuthenticationTypeMSN
},
216 { &kSecAttrAuthenticationTypeDPA
, kSecAuthenticationTypeDPA
},
217 { &kSecAttrAuthenticationTypeRPA
, kSecAuthenticationTypeRPA
},
218 { &kSecAttrAuthenticationTypeHTTPBasic
, kSecAuthenticationTypeHTTPBasic
},
219 { &kSecAttrAuthenticationTypeHTTPDigest
, kSecAuthenticationTypeHTTPDigest
},
220 { &kSecAttrAuthenticationTypeHTMLForm
, kSecAuthenticationTypeHTMLForm
},
221 { &kSecAttrAuthenticationTypeDefault
, kSecAuthenticationTypeDefault
}
224 static const int kNumberOfAuthenticationTypes
= sizeof(gAuthTypes
) / sizeof(AuthenticationAttributeInfo
);
227 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
228 * SecAttrAuthenticationType to a SecAuthenticationType.
230 static SecAuthenticationType
231 _SecAuthenticationTypeForSecAttrAuthenticationType(
232 CFTypeRef authenticationType
)
234 SecAuthenticationType result
= kSecAuthenticationTypeAny
;
236 if (authenticationType
!= NULL
) {
238 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
239 if (CFEqual(authenticationType
, *(gAuthTypes
[count
].authValue
))) {
240 result
= gAuthTypes
[count
].authType
;
250 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
251 * to a SecAttrAuthenticationType.
254 _SecAttrAuthenticationTypeForSecAuthenticationType(
255 SecAuthenticationType authenticationType
)
257 CFTypeRef result
= NULL
;
259 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
260 if (gAuthTypes
[count
].authType
== authenticationType
) {
261 result
= *(gAuthTypes
[count
].authValue
);
270 /******************************************************************************/
272 struct KeyAlgorithmInfo
{
273 const CFStringRef
*keyType
;
277 static KeyAlgorithmInfo gKeyTypes
[] = {
278 { &kSecAttrKeyTypeRSA
, CSSM_ALGID_RSA
},
279 { &kSecAttrKeyTypeDSA
, CSSM_ALGID_DSA
},
280 { &kSecAttrKeyTypeAES
, CSSM_ALGID_AES
},
281 { &kSecAttrKeyTypeDES
, CSSM_ALGID_DES
},
282 { &kSecAttrKeyType3DES
, CSSM_ALGID_3DES
},
283 { &kSecAttrKeyTypeRC4
, CSSM_ALGID_RC4
},
284 { &kSecAttrKeyTypeRC2
, CSSM_ALGID_RC2
},
285 { &kSecAttrKeyTypeCAST
, CSSM_ALGID_CAST
},
286 { &kSecAttrKeyTypeECDSA
, CSSM_ALGID_ECDSA
},
287 { &kSecAttrKeyTypeEC
, CSSM_ALGID_ECDSA
}
290 static const int kNumberOfKeyTypes
= sizeof(gKeyTypes
) / sizeof (KeyAlgorithmInfo
);
293 static UInt32
_SecAlgorithmTypeFromSecAttrKeyType(
294 CFTypeRef keyTypeRef
)
296 UInt32 keyAlgValue
= 0;
297 if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef
))
301 for (ix
=0; ix
<kNumberOfKeyTypes
; ix
++) {
302 if (CFEqual(keyTypeRef
, *(gKeyTypes
[ix
].keyType
))) {
303 keyAlgValue
= gKeyTypes
[ix
].keyValue
;
308 //%%%TODO try to convert the input string to a number here
314 enum ItemRepresentation
316 kStringRepresentation
,
318 kNumberRepresentation
,
319 kBooleanRepresentation
,
324 struct InternalAttributeListInfo
327 const CFStringRef
*newItemType
;
328 ItemRepresentation itemRepresentation
;
332 static InternalAttributeListInfo gGenericPasswordAttributes
[] =
334 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
335 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
336 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
337 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
338 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
339 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
340 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
341 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
342 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
343 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
344 { kSecServiceItemAttr
, &kSecAttrService
, kStringRepresentation
},
345 { kSecGenericItemAttr
, &kSecAttrGeneric
, kDataRepresentation
}
348 static const int kNumberOfGenericPasswordAttributes
= sizeof(gGenericPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
351 static InternalAttributeListInfo gInternetPasswordAttributes
[] =
353 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
354 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
355 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
356 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
357 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
358 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
359 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
360 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
361 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
362 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
363 { kSecSecurityDomainItemAttr
, &kSecAttrSecurityDomain
, kStringRepresentation
},
364 { kSecServerItemAttr
, &kSecAttrServer
, kStringRepresentation
},
365 { kSecAuthenticationTypeItemAttr
, &kSecAttrAuthenticationType
, kStringRepresentation
}, // maps from UInt32 value to string constant
366 { kSecPortItemAttr
, &kSecAttrPort
, kNumberRepresentation
},
367 { kSecPathItemAttr
, &kSecAttrPath
, kStringRepresentation
}
370 static const int kNumberOfInternetPasswordAttributes
= sizeof(gInternetPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
373 static InternalAttributeListInfo gCertificateAttributes
[] =
375 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
376 { kSecSubjectItemAttr
, &kSecAttrSubject
, kDataRepresentation
},
377 { kSecIssuerItemAttr
, &kSecAttrIssuer
, kDataRepresentation
},
378 { kSecSerialNumberItemAttr
, &kSecAttrSerialNumber
, kDataRepresentation
},
379 { kSecPublicKeyHashItemAttr
, &kSecAttrPublicKeyHash
, kDataRepresentation
},
380 { kSecSubjectKeyIdentifierItemAttr
, &kSecAttrSubjectKeyID
, kDataRepresentation
},
381 { kSecCertTypeItemAttr
, &kSecAttrCertificateType
, kDataRepresentation
},
382 { kSecCertEncodingItemAttr
, &kSecAttrCertificateEncoding
, kDataRepresentation
}
385 static const int kNumberOfCertificateAttributes
= sizeof(gCertificateAttributes
) / sizeof(InternalAttributeListInfo
);
388 static InternalAttributeListInfo gKeyAttributes
[] =
390 { kSecKeyKeyClass
, &kSecAttrKeyClass
, kStringRepresentation
}, // key class maps from UInt32 value to string constant
391 { kSecKeyPrintName
, &kSecAttrLabel
, kStringRepresentation
}, // note that "print name" maps to the user-visible label
392 // { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
393 { kSecKeyPermanent
, &kSecAttrIsPermanent
, kBooleanRepresentation
},
394 // { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
395 // { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
396 { kSecKeyLabel
, &kSecAttrApplicationLabel
, kDataRepresentation
}, // this contains the hash of the key (or the public key hash, if asymmetric) as a CFData. Legacy keys may contain a UUID as a CFString
397 { kSecKeyApplicationTag
, &kSecAttrApplicationTag
, kDataRepresentation
},
398 // { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
399 { kSecKeyKeyType
, &kSecAttrKeyType
, kStringRepresentation
}, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
400 { kSecKeyKeySizeInBits
, &kSecAttrKeySizeInBits
, kNumberRepresentation
},
401 { kSecKeyEffectiveKeySize
, &kSecAttrEffectiveKeySize
, kNumberRepresentation
},
402 // { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
403 // { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
404 // { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
405 // { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
406 // { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
407 // { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
408 { kSecKeyEncrypt
, &kSecAttrCanEncrypt
, kBooleanRepresentation
},
409 { kSecKeyDecrypt
, &kSecAttrCanDecrypt
, kBooleanRepresentation
},
410 { kSecKeyDerive
, &kSecAttrCanDerive
, kBooleanRepresentation
},
411 { kSecKeySign
, &kSecAttrCanSign
, kBooleanRepresentation
},
412 { kSecKeyVerify
, &kSecAttrCanVerify
, kBooleanRepresentation
},
413 // { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
414 // { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
415 { kSecKeyWrap
, &kSecAttrCanWrap
, kBooleanRepresentation
},
416 { kSecKeyUnwrap
, &kSecAttrCanUnwrap
, kBooleanRepresentation
}
419 static const int kNumberOfKeyAttributes
= sizeof(gKeyAttributes
) / sizeof(InternalAttributeListInfo
);
422 static void* CloneDataByType(ItemRepresentation type
, CFTypeRef value
, UInt32
& length
)
426 case kStringRepresentation
:
428 if (CFStringGetTypeID() != CFGetTypeID(value
)) {
432 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
433 char* buffer
= (char*) malloc(maxLength
);
434 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
436 length
= (UInt32
)strlen(buffer
);
446 case kDataRepresentation
:
448 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
449 // We may have a string here, since the key label may be a GUID for the symmetric keys
450 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
451 char* buffer
= (char*) malloc(maxLength
);
452 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
454 length
= (UInt32
)strlen(buffer
);
464 if (CFDataGetTypeID() != CFGetTypeID(value
)) {
468 length
= (UInt32
)CFDataGetLength((CFDataRef
) value
);
469 uint8_t* buffer
= (uint8_t*) malloc(length
);
470 CFDataGetBytes((CFDataRef
) value
, CFRangeMake(0, length
), buffer
);
474 case kNumberRepresentation
:
476 if (CFNumberGetTypeID() != CFGetTypeID(value
)) {
480 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
481 Boolean converted
= CFNumberGetValue((CFNumberRef
) value
, kCFNumberSInt32Type
, buffer
);
483 length
= sizeof(uint32_t);
493 case kBooleanRepresentation
:
495 if (CFBooleanGetTypeID() != CFGetTypeID(value
)) {
499 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
500 *buffer
= (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
501 length
= sizeof(uint32_t);
505 case kDateRepresentation
:
507 if (CFDateGetTypeID() != CFGetTypeID(value
)) {
511 char* buffer
= (char*) calloc(1, 32); // max length of a CSSM date string
512 CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef
) value
, buffer
);
513 length
= (UInt32
)strlen(buffer
);
527 _ConvertNewFormatToOldFormat(
528 CFAllocatorRef allocator
,
529 const InternalAttributeListInfo
* info
,
531 CFDictionaryRef dictionaryRef
,
532 SecKeychainAttributeList
* &attrList
535 // get the keychain attributes array from the data item
536 // here's the problem. On the one hand, we have a dictionary that is purported to contain
537 // attributes for our type. On the other hand, the dictionary may contain items we don't support,
538 // and we therefore don't know how many attributes we will have unless we count them first
541 attrList
= (SecKeychainAttributeList
*) calloc(1, sizeof(SecKeychainAttributeList
));
543 // make storage to extract the dictionary items
544 CFIndex itemsInDictionary
= CFDictionaryGetCount(dictionaryRef
);
545 CFTypeRef keys
[itemsInDictionary
];
546 CFTypeRef values
[itemsInDictionary
];
548 CFTypeRef
*keysPtr
= keys
;
549 CFTypeRef
*valuesPtr
= values
;
551 CFDictionaryGetKeysAndValues(dictionaryRef
, keys
, values
);
553 // count the number of items we are interested in
557 // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
558 // we don't pay the price for this twice
559 SecKeychainAttrType tags
[itemsInDictionary
];
560 ItemRepresentation types
[itemsInDictionary
];
562 for (i
= 0; i
< itemsInDictionary
; ++i
)
564 CFTypeRef key
= keysPtr
[i
];
567 for (j
= 0; j
< infoNumItems
; ++j
)
569 if (CFEqual(*(info
[j
].newItemType
), key
))
571 tags
[i
] = info
[j
].oldItemType
;
572 types
[i
] = info
[j
].itemRepresentation
;
578 if (j
>= infoNumItems
)
580 // if we got here, we aren't interested in this item.
585 // now we can make the result array
586 attrList
->count
= (UInt32
)count
;
587 attrList
->attr
= (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * count
);
589 // fill out the array
590 int resultPointer
= 0;
591 for (i
= 0; i
< itemsInDictionary
; ++i
)
593 if (values
[i
] != NULL
)
595 attrList
->attr
[resultPointer
].tag
= tags
[i
];
597 // we have to clone the data pointer. The caller will need to make sure to throw these away
598 // with _FreeAttrList when it is done...
599 attrList
->attr
[resultPointer
].data
= CloneDataByType(types
[i
], valuesPtr
[i
], attrList
->attr
[resultPointer
].length
);
604 return errSecSuccess
;
610 _ConvertOldFormatToNewFormat(
611 CFAllocatorRef allocator
,
612 const InternalAttributeListInfo
* info
,
614 SecKeychainItemRef itemRef
,
615 CFMutableDictionaryRef
& dictionaryRef
)
617 SecKeychainAttributeList list
;
618 list
.count
= infoNumItems
;
619 list
.attr
= (SecKeychainAttribute
*) calloc(infoNumItems
, sizeof(SecKeychainAttribute
));
621 // fill out the array. We only need to fill in the tags, since calloc zeros what it returns
623 for (i
= 0; i
< infoNumItems
; ++i
)
625 list
.attr
[i
].tag
= info
[i
].oldItemType
;
628 OSStatus result
= SecKeychainItemCopyContent(itemRef
, NULL
, &list
, NULL
, NULL
);
629 if (result
!= errSecSuccess
)
631 dictionaryRef
= NULL
;
636 // create the dictionary
637 dictionaryRef
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
640 for (i
= 0; i
< infoNumItems
; ++i
)
642 if (list
.attr
[i
].data
== NULL
)
645 switch (info
[i
].itemRepresentation
)
647 case kStringRepresentation
:
649 CFStringRef stringRef
;
650 if (info
[i
].oldItemType
== kSecKeyKeyClass
) {
651 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
652 uint32_t keyRecordValue
= *((uint32_t*)list
.attr
[i
].data
);
653 bool retainString
= true;
654 switch (keyRecordValue
) {
655 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
656 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
658 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
659 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
661 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
662 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
665 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyRecordValue
);
669 if (retainString
) CFRetain(stringRef
);
670 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
671 CFRelease(stringRef
);
674 else if (info
[i
].oldItemType
== kSecKeyKeyType
) {
675 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
676 uint32_t keyAlgValue
= *((uint32_t*)list
.attr
[i
].data
);
677 bool retainString
= true;
678 switch (keyAlgValue
) {
679 case CSSM_ALGID_RSA
:
680 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
682 case CSSM_ALGID_DSA
:
683 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
685 case CSSM_ALGID_AES
:
686 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
688 case CSSM_ALGID_DES
:
689 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
691 case CSSM_ALGID_3DES
:
692 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
694 case CSSM_ALGID_RC4
:
695 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
697 case CSSM_ALGID_RC2
:
698 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
700 case CSSM_ALGID_CAST
:
701 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
703 case CSSM_ALGID_ECDSA
:
704 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
707 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyAlgValue
);
708 retainString
= false;
712 if (retainString
) CFRetain(stringRef
);
713 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
714 CFRelease(stringRef
);
718 // normal case: attribute contains a string
719 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
720 if (stringRef
== NULL
)
721 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
722 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
723 CFRelease(stringRef
);
728 case kDataRepresentation
:
730 if ((info
[i
].oldItemType
== kSecKeyLabel
) && (list
.attr
[i
].length
== kUUIDStringLength
)) {
731 // It's possible that there could be a string here because the key label may have a UUID
732 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
733 if (stringRef
== NULL
)
734 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
735 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
736 CFRelease(stringRef
);
739 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*) list
.attr
[i
].data
, list
.attr
[i
].length
);
741 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
742 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dataRef
);
747 case kNumberRepresentation
:
749 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, list
.attr
[i
].data
);
750 if (numberRef
== NULL
)
751 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
752 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), numberRef
);
753 CFRelease(numberRef
);
757 case kBooleanRepresentation
:
759 uint32_t value
= *((uint32_t*)list
.attr
[i
].data
);
760 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
761 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), boolRef
);
765 case kDateRepresentation
:
767 CFDateRef dateRef
= NULL
;
768 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list
.attr
[i
].data
, list
.attr
[i
].length
, &dateRef
);
770 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
771 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dateRef
);
779 SecKeychainItemFreeContent(&list
, NULL
);
789 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
790 * attributes of item.
793 _CreateAttributesDictionaryFromGenericPasswordItem(
794 CFAllocatorRef allocator
,
795 SecKeychainItemRef item
,
796 CFDictionaryRef
*dictionary
)
798 // do the basic allocations
799 CFMutableDictionaryRef dict
= NULL
;
800 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, item
, dict
);
801 if (result
== errSecSuccess
) // did we complete OK
803 CFDictionaryAddValue(dict
, kSecClass
, kSecClassGenericPassword
);
814 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
815 * attributes of item.
818 _CreateAttributesDictionaryFromCertificateItem(
819 CFAllocatorRef allocator
,
820 SecKeychainItemRef item
,
821 CFDictionaryRef
*dictionary
)
823 // do the basic allocations
824 CFMutableDictionaryRef dict
= NULL
;
825 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gCertificateAttributes
, kNumberOfCertificateAttributes
, item
, dict
);
826 if (result
== errSecSuccess
) // did we complete OK
828 CFDictionaryAddValue(dict
, kSecClass
, kSecClassCertificate
);
833 return errSecSuccess
;
837 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
838 * attributes of item.
841 _CreateAttributesDictionaryFromKeyItem(
842 CFAllocatorRef allocator
,
843 SecKeychainItemRef item
,
844 CFDictionaryRef
*dictionary
)
847 //%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
848 // Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
849 // SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
852 goto error_exit
; // unable to get the attribute info (i.e. database schema)
855 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
857 // do the basic allocations
858 CFMutableDictionaryRef dict
= NULL
;
859 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gKeyAttributes
, kNumberOfKeyAttributes
, item
, dict
);
860 if (result
== errSecSuccess
) // did we complete OK
862 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
867 return errSecSuccess
;
870 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
872 SecItemClass itemClass
= 0;
874 SecKeychainAttributeList
*attrList
= NULL
;
875 SecKeychainAttributeInfo
*info
= NULL
;
876 SecKeychainRef keychain
= NULL
;
878 OSStatus status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
880 goto error_exit
; // item must have an itemClass
885 case kSecInternetPasswordItemClass
:
886 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
888 case kSecGenericPasswordItemClass
:
889 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
891 case 'ashp': /* kSecAppleSharePasswordItemClass */
892 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
899 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
901 goto error_exit
; // item must have a keychain, so we can get the attribute info for it
904 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
906 goto error_exit
; // unable to get the attribute info (i.e. database schema)
909 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
911 goto error_exit
; // unable to get the attribute info (i.e. database schema)
914 for (ix
= 0; ix
< info
->count
; ++ix
)
916 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
917 if (!attribute
->length
&& !attribute
->data
)
920 UInt32 j
, count
= kNumberOfKeyAttributes
;
921 InternalAttributeListInfo
*intInfo
= NULL
;
922 for (j
=0; j
<count
; j
++) {
923 if (gKeyAttributes
[j
].oldItemType
== info
->tag
[ix
]) {
924 intInfo
= &gKeyAttributes
[j
];
931 switch (intInfo
->itemRepresentation
)
933 case kStringRepresentation
:
935 CFStringRef stringRef
;
936 if (intInfo
->oldItemType
== kSecKeyKeyClass
) {
937 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
938 UInt32 keyRecordValue
= *((UInt32
*)attribute
->data
);
939 bool retainString
= true;
940 switch (keyRecordValue
) {
941 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
942 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
944 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
945 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
947 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
948 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
951 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyRecordValue
);
955 if (retainString
) CFRetain(stringRef
);
956 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
957 CFRelease(stringRef
);
960 else if (intInfo
->oldItemType
== kSecKeyKeyType
) {
961 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
962 UInt32 keyAlgValue
= *((UInt32
*)attribute
->data
);
963 bool retainString
= true;
964 switch (keyAlgValue
) {
965 case CSSM_ALGID_RSA
:
966 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
968 case CSSM_ALGID_DSA
:
969 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
971 case CSSM_ALGID_AES
:
972 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
974 case CSSM_ALGID_DES
:
975 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
977 case CSSM_ALGID_3DES
:
978 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
980 case CSSM_ALGID_RC4
:
981 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
983 case CSSM_ALGID_RC2
:
984 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
986 case CSSM_ALGID_CAST
:
987 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
989 case CSSM_ALGID_ECDSA
:
990 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
993 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyAlgValue
);
994 retainString
= false;
998 if (retainString
) CFRetain(stringRef
);
999 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1000 CFRelease(stringRef
);
1004 // normal case: attribute contains a string
1005 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1006 if (stringRef
== NULL
)
1007 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1008 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1009 CFRelease(stringRef
);
1014 case kDataRepresentation
:
1016 if ((intInfo
->oldItemType
== kSecKeyLabel
) && (attribute
->length
== kUUIDStringLength
)) {
1017 // It's possible that there could be a string here because the key label may have a UUID
1018 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1019 if (stringRef
== NULL
)
1020 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1021 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1022 CFRelease(stringRef
);
1026 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)attribute
->data
, attribute
->length
);
1027 if (dataRef
== NULL
)
1028 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
1029 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dataRef
);
1034 case kNumberRepresentation
:
1036 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attribute
->data
);
1037 if (numberRef
== NULL
)
1038 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
1039 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), numberRef
);
1040 CFRelease(numberRef
);
1044 case kBooleanRepresentation
:
1046 UInt32 value
= *((UInt32
*)attribute
->data
);
1047 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1048 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), boolRef
);
1052 case kDateRepresentation
:
1054 //%%% FIXME need to convert from a CSSM date string to a CFDateRef here
1055 CFDateRef dateRef
= NULL
;
1056 if (dateRef
== NULL
)
1057 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
1058 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dateRef
);
1065 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
1070 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
1073 SecKeychainFreeAttributeInfo(info
);
1076 CFRelease(keychain
);
1085 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
1086 * attributes of item.
1089 _CreateAttributesDictionaryFromInternetPasswordItem(
1090 CFAllocatorRef allocator
,
1091 SecKeychainItemRef item
,
1092 CFDictionaryRef
*dictionary
)
1095 SecKeychainAttribute attr
[] = {
1096 { kSecServerItemAttr
, 0, NULL
}, /* [0] server */
1097 { kSecSecurityDomainItemAttr
, 0, NULL
}, /* [1] securityDomain */
1098 { kSecAccountItemAttr
, 0, NULL
}, /* [2] account */
1099 { kSecPathItemAttr
, 0, NULL
}, /* [3] path */
1100 { kSecPortItemAttr
, 0, NULL
}, /* [4] port */
1101 { kSecProtocolItemAttr
, 0, NULL
}, /* [5] protocol */
1102 { kSecAuthenticationTypeItemAttr
, 0, NULL
}, /* [6] authenticationType */
1103 { kSecCommentItemAttr
, 0, NULL
}, /* [7] comment */
1104 { kSecDescriptionItemAttr
, 0, NULL
}, /* [8] description */
1105 { kSecLabelItemAttr
, 0, NULL
}, /* [9] label */
1106 { kSecCreationDateItemAttr
, 0, NULL
}, /* [10] creation date */
1107 { kSecModDateItemAttr
, 0, NULL
}, /* [11] modification date */
1108 { kSecCreatorItemAttr
, 0, NULL
}, /* [12] creator */
1109 { kSecTypeItemAttr
, 0, NULL
}, /* [13] type */
1110 { kSecInvisibleItemAttr
, 0, NULL
}, /* [14] invisible */
1111 { kSecNegativeItemAttr
, 0, NULL
}, /* [15] negative */
1113 SecKeychainAttributeList attrList
= { sizeof(attr
) / sizeof(SecKeychainAttribute
), attr
};
1116 CFTypeRef keys
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1117 CFTypeRef values
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1121 // copy the item's attributes
1122 status
= SecKeychainItemCopyContent(item
, NULL
, &attrList
, NULL
, NULL
);
1123 require_noerr(status
, SecKeychainItemCopyContent_failed
);
1128 keys
[numValues
] = kSecClass
;
1129 values
[numValues
] = kSecClassInternetPassword
;
1132 // add kSecAttrServer
1133 if ( attrList
.attr
[0].length
> 0 ) {
1134 keys
[numValues
] = kSecAttrServer
;
1135 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[0].data
, attrList
.attr
[0].length
, kCFStringEncodingUTF8
, FALSE
);
1136 if ( values
[numValues
] != NULL
) {
1141 // add kSecAttrSecurityDomain
1142 if ( attrList
.attr
[1].length
> 0 ) {
1143 keys
[numValues
] = kSecAttrSecurityDomain
;
1144 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[1].data
, attrList
.attr
[1].length
, kCFStringEncodingUTF8
, FALSE
);
1145 if ( values
[numValues
] != NULL
) {
1150 // add kSecAttrAccount
1151 if ( attrList
.attr
[2].length
> 0 ) {
1152 keys
[numValues
] = kSecAttrAccount
;
1153 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[2].data
, attrList
.attr
[2].length
, kCFStringEncodingUTF8
, FALSE
);
1154 if ( values
[numValues
] != NULL
) {
1160 if ( attrList
.attr
[3].length
> 0 ) {
1161 keys
[numValues
] = kSecAttrPath
;
1162 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[3].data
, attrList
.attr
[3].length
, kCFStringEncodingUTF8
, FALSE
);
1163 if ( values
[numValues
] != NULL
) {
1169 if ( attrList
.attr
[4].length
> 0 ) {
1170 keys
[numValues
] = kSecAttrPort
;
1171 values
[numValues
] = CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[4].data
);
1172 if ( values
[numValues
] != NULL
) {
1177 // add kSecAttrProtocol
1178 if ( attrList
.attr
[5].length
> 0 ) {
1179 keys
[numValues
] = kSecAttrProtocol
;
1180 values
[numValues
] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType
*)attrList
.attr
[5].data
);
1181 if ( values
[numValues
] != NULL
) {
1182 CFRetain(values
[numValues
]);
1187 // add kSecAttrAuthenticationType
1188 if ( attrList
.attr
[6].length
> 0 ) {
1189 keys
[numValues
] = kSecAttrAuthenticationType
;
1190 values
[numValues
] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType
*)attrList
.attr
[6].data
);
1191 if ( values
[numValues
] != NULL
) {
1192 CFRetain(values
[numValues
]);
1197 // add kSecAttrComment
1198 if ( attrList
.attr
[7].length
> 0 ) {
1199 keys
[numValues
] = kSecAttrComment
;
1200 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[7].data
, attrList
.attr
[7].length
, kCFStringEncodingUTF8
, FALSE
);
1201 if ( values
[numValues
] != NULL
) {
1206 // add kSecAttrDescription
1207 if ( attrList
.attr
[8].length
> 0 ) {
1208 keys
[numValues
] = kSecAttrDescription
;
1209 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[8].data
, attrList
.attr
[8].length
, kCFStringEncodingUTF8
, FALSE
);
1210 if ( values
[numValues
] != NULL
) {
1215 // add kSecAttrLabel
1216 if ( attrList
.attr
[9].length
> 0 ) {
1217 keys
[numValues
] = kSecAttrLabel
;
1218 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[9].data
, attrList
.attr
[9].length
, kCFStringEncodingUTF8
, FALSE
);
1219 if ( values
[numValues
] != NULL
) {
1224 // add kSecAttrCreationDate
1225 if ( attrList
.attr
[10].length
> 0 ) {
1226 CFDateRef creationDate
= NULL
;
1227 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[10].data
, attrList
.attr
[10].length
, &creationDate
);
1228 keys
[numValues
] = kSecAttrCreationDate
;
1229 values
[numValues
] = creationDate
;
1230 if ( values
[numValues
] != NULL
) {
1235 // add kSecAttrModificationDate
1236 if ( attrList
.attr
[11].length
> 0 ) {
1237 CFDateRef modDate
= NULL
;
1238 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[11].data
, attrList
.attr
[11].length
, &modDate
);
1239 keys
[numValues
] = kSecAttrModificationDate
;
1240 values
[numValues
] = modDate
;
1241 if ( values
[numValues
] != NULL
) {
1246 // add kSecCreatorItemAttr
1247 if ( attrList
.attr
[12].length
> 0 ) {
1248 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[12].data
);
1249 keys
[numValues
] = kSecAttrCreator
;
1250 values
[numValues
] = numberRef
;
1251 if ( values
[numValues
] != NULL
) {
1252 CFRetain(values
[numValues
]);
1257 // add kSecTypeItemAttr
1258 if ( attrList
.attr
[13].length
> 0 ) {
1259 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[13].data
);
1260 keys
[numValues
] = kSecAttrType
;
1261 values
[numValues
] = numberRef
;
1262 if ( values
[numValues
] != NULL
) {
1263 CFRetain(values
[numValues
]);
1268 // add kSecInvisibleItemAttr
1269 if ( attrList
.attr
[14].length
> 0 ) {
1270 uint32_t value
= *((uint32_t*)attrList
.attr
[14].data
);
1271 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1272 keys
[numValues
] = kSecAttrIsInvisible
;
1273 values
[numValues
] = boolRef
;
1274 if ( values
[numValues
] != NULL
) {
1275 CFRetain(values
[numValues
]);
1280 // add kSecNegativeItemAttr
1281 if ( attrList
.attr
[15].length
> 0 ) {
1282 uint32_t value
= *((uint32_t*)attrList
.attr
[15].data
);
1283 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1284 keys
[numValues
] = kSecAttrIsNegative
;
1285 values
[numValues
] = boolRef
;
1286 if ( values
[numValues
] != NULL
) {
1287 CFRetain(values
[numValues
]);
1292 // create the dictionary
1293 *dictionary
= CFDictionaryCreate(allocator
, keys
, values
, numValues
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1295 // release the values added to the dictionary
1296 for ( index
= 0; index
< numValues
; ++index
)
1298 CFRelease(values
[index
]);
1301 // and free the attributes
1302 (void) SecKeychainItemFreeContent(&attrList
, NULL
);
1304 SecKeychainItemCopyContent_failed
:
1311 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
1312 * attributes of the specified item class and item.
1315 _CreateAttributesDictionaryFromItem(
1316 CFAllocatorRef allocator
,
1317 SecItemClass itemClass
,
1318 SecKeychainItemRef item
,
1319 CFDictionaryRef
*dictionary
)
1323 case kSecInternetPasswordItemClass
:
1324 return _CreateAttributesDictionaryFromInternetPasswordItem(allocator
, item
, dictionary
);
1326 case kSecGenericPasswordItemClass
:
1327 return _CreateAttributesDictionaryFromGenericPasswordItem(allocator
, item
, dictionary
);
1329 case kSecCertificateItemClass
:
1330 return _CreateAttributesDictionaryFromCertificateItem(allocator
, item
, dictionary
);
1332 case kSecPublicKeyItemClass
:
1333 case kSecPrivateKeyItemClass
:
1334 case kSecSymmetricKeyItemClass
:
1335 return _CreateAttributesDictionaryFromKeyItem(allocator
, item
, dictionary
);
1346 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
1347 * by the _CreateSecKeychainAttributeListFromDictionary function.
1351 SecKeychainAttributeList
*attrListPtr
)
1355 if ( attrListPtr
!= NULL
) {
1356 if ( attrListPtr
->attr
!= NULL
) {
1357 // free any attribute data
1358 for ( index
= 0; index
< attrListPtr
->count
; ++index
) {
1359 free(attrListPtr
->attr
[index
].data
);
1361 // free the attribute array
1362 free(attrListPtr
->attr
);
1364 // free the attribute list
1370 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
1371 * attr using the data and tag parameters.
1373 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1374 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1377 _CFDataCreateAttribute(
1379 SecKeychainAttrType tag
,
1380 SecKeychainAttributePtr attr
)
1382 OSStatus status
= errSecSuccess
;
1385 // set the attribute tag
1388 // determine the attribute length
1389 attr
->length
= (UInt32
) CFDataGetLength(data
);
1390 range
= CFRangeMake(0, (CFIndex
)attr
->length
);
1392 // allocate memory for the attribute bytes
1393 attr
->data
= malloc(attr
->length
);
1394 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1396 // get the attribute bytes
1397 CFDataGetBytes(data
, range
, (UInt8
*)attr
->data
);
1405 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
1406 * attr using the string and tag parameters.
1408 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1409 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1412 _CFStringCreateAttribute(
1414 SecKeychainAttrType tag
,
1415 SecKeychainAttributePtr attr
)
1417 OSStatus status
= errSecSuccess
;
1420 // set the attribute tag
1423 // determine the attribute length
1424 range
= CFRangeMake(0, CFStringGetLength(string
));
1425 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, (CFIndex
*)&attr
->length
);
1427 // allocate memory for the attribute bytes
1428 attr
->data
= malloc(attr
->length
);
1429 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1431 // get the attribute bytes
1432 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*)attr
->data
, attr
->length
, NULL
);
1441 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1442 * from the attribute key/values in attrDictionary.
1444 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1445 * must be freed by the caller with _FreeAttrList()
1448 _CreateSecKeychainGenericPasswordAttributeListFromDictionary(
1449 CFDictionaryRef attrDictionary
,
1450 SecKeychainAttributeList
**attrList
)
1452 return _ConvertNewFormatToOldFormat(NULL
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, attrDictionary
, *attrList
);
1457 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
1458 * from the attribute key/values in attrDictionary.
1460 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1461 * must be freed by the caller with _FreeAttrList()
1464 _CreateSecKeychainCertificateAttributeListFromDictionary(
1465 CFDictionaryRef attrDictionary
,
1466 SecKeychainAttributeList
**attrList
)
1468 return _ConvertNewFormatToOldFormat(NULL
, gCertificateAttributes
, kNumberOfCertificateAttributes
, attrDictionary
, *attrList
);
1473 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
1474 * from the attribute key/values in attrDictionary.
1476 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1477 * must be freed by the caller with _FreeAttrList()
1480 _CreateSecKeychainKeyAttributeListFromDictionary(
1481 CFDictionaryRef attrDictionary
,
1482 SecKeychainAttributeList
**attrList
)
1485 //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
1486 return _ConvertNewFormatToOldFormat(NULL
, gKeyAttributes
, kNumberOfKeyAttributes
, attrDictionary
, *attrList
);
1488 // explicitly build attribute list for supported key attributes
1489 // NOTE: this code supports only MaxSecKeyAttributes (15) attributes
1490 const int MaxSecKeyAttributes
= 15;
1494 SecKeychainAttributeList
*attrListPtr
;
1496 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1497 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1499 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeyAttributes
, sizeof(SecKeychainAttribute
));
1500 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1502 // [0] get the kSecKeyKeyClass value
1503 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyClass
, (const void **)&value
) && value
) {
1504 UInt32 keyRecordValue
= 0;
1505 if (CFEqual(kSecAttrKeyClassPublic
, value
))
1506 keyRecordValue
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
1507 else if (CFEqual(kSecAttrKeyClassPrivate
, value
))
1508 keyRecordValue
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
1509 else if (CFEqual(kSecAttrKeyClassSymmetric
, value
))
1510 keyRecordValue
= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
;
1512 // only use this attribute if we recognize the value!
1513 if (keyRecordValue
!= 0) {
1514 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1515 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1517 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyClass
;
1518 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1519 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyRecordValue
;
1521 ++attrListPtr
->count
;
1525 // [1] get the kSecKeyPrintName string
1526 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) && value
) {
1527 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyPrintName
, &attrListPtr
->attr
[attrListPtr
->count
]);
1528 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1530 ++attrListPtr
->count
;
1533 // [2] get the kSecKeyPermanent boolean
1534 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsPermanent
, (const void **)&value
) && value
) {
1535 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1536 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1538 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyPermanent
;
1539 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1540 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1542 ++attrListPtr
->count
;
1545 // [3] get the kSecKeyLabel string
1546 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationLabel
, (const void **)&value
) && value
) {
1547 if (CFStringGetTypeID() == CFGetTypeID(value
))
1548 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1549 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1550 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1552 status
= errSecParam
;
1554 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1556 ++attrListPtr
->count
;
1559 // [4] get the kSecKeyApplicationTag data
1560 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationTag
, (const void **)&value
) && value
) {
1561 if (CFStringGetTypeID() == CFGetTypeID(value
))
1562 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1563 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1564 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1566 status
= errSecParam
;
1568 require_noerr_quiet(status
, CFDataCreateAttribute_failed
);
1569 ++attrListPtr
->count
;
1572 // [5] get the kSecKeyKeyType number
1573 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyType
, (const void **)&value
) && value
) {
1574 UInt32 keyAlgValue
= _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType
);
1575 if (keyAlgValue
!= 0) {
1576 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1577 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1579 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyType
;
1580 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1581 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyAlgValue
;
1583 ++attrListPtr
->count
;
1587 // [6] get the kSecKeyKeySizeInBits number
1588 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeySizeInBits
, (const void **)&value
) && value
) {
1589 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1590 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1591 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1593 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeySizeInBits
;
1594 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1595 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1597 ++attrListPtr
->count
;
1601 // [7] get the kSecKeyEffectiveKeySize number
1602 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrEffectiveKeySize
, (const void **)&value
) && value
) {
1603 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1604 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1605 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1607 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEffectiveKeySize
;
1608 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1609 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1611 ++attrListPtr
->count
;
1615 // [8] get the kSecKeyEncrypt boolean
1616 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanEncrypt
, (const void **)&value
) && value
) {
1617 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1618 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1619 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1621 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEncrypt
;
1622 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1623 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1625 ++attrListPtr
->count
;
1629 // [9] get the kSecKeyDecrypt boolean
1630 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDecrypt
, (const void **)&value
) && value
) {
1631 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1632 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1633 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1635 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDecrypt
;
1636 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1637 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1639 ++attrListPtr
->count
;
1643 // [10] get the kSecKeyDerive boolean
1644 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDerive
, (const void **)&value
) && value
) {
1645 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1646 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1647 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1649 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDerive
;
1650 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1651 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1653 ++attrListPtr
->count
;
1657 // [11] get the kSecKeySign boolean
1658 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanSign
, (const void **)&value
) && value
) {
1659 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1660 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1661 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1663 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeySign
;
1664 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1665 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1667 ++attrListPtr
->count
;
1671 // [12] get the kSecKeyVerify boolean
1672 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanVerify
, (const void **)&value
) && value
) {
1673 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1674 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1675 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1677 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyVerify
;
1678 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1679 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1681 ++attrListPtr
->count
;
1685 // [13] get the kSecKeyWrap boolean
1686 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanWrap
, (const void **)&value
) && value
) {
1687 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1688 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1689 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1691 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyWrap
;
1692 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1693 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1695 ++attrListPtr
->count
;
1699 // [14] get the kSecKeyUnwrap boolean
1700 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanUnwrap
, (const void **)&value
) && value
) {
1701 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1702 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1703 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1705 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyUnwrap
;
1706 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1707 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1709 ++attrListPtr
->count
;
1713 // return the pointer to the attrList
1714 *attrList
= attrListPtr
;
1716 return ( errSecSuccess
);
1720 malloc_number_failed
:
1721 CFDataCreateAttribute_failed
:
1722 CFStringCreateAttribute_failed
:
1723 malloc_attrPtr_failed
:
1725 // free any attributes
1726 _FreeAttrList(attrListPtr
);
1728 calloc_attrListPtr_failed
:
1730 return ( errSecBufferTooSmall
);
1735 static CFTypeRef
copyNumber(CFTypeRef obj
)
1740 CFTypeID tid
= CFGetTypeID(obj
);
1741 if (tid
== CFNumberGetTypeID())
1747 if (tid
== CFBooleanGetTypeID())
1749 SInt32 value
= CFBooleanGetValue((CFBooleanRef
)obj
);
1750 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1753 if (tid
== CFStringGetTypeID())
1755 SInt32 value
= CFStringGetIntValue((CFStringRef
)obj
);
1756 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
1757 /* If a string converted to an int isn't equal to the int printed as
1758 a string, return a NULL instead. */
1759 if (!CFEqual(t
, obj
))
1765 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1771 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1772 * from the attribute key/values in attrDictionary.
1774 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1775 * must be freed by the caller with _FreeAttrList()
1778 _CreateSecKeychainInternetPasswordAttributeListFromDictionary(
1779 CFDictionaryRef attrDictionary
,
1780 SecKeychainAttributeList
**attrList
)
1782 // explicitly build attribute list for supported key attributes
1783 // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
1784 const int MaxSecKeychainAttributes
= 14;
1788 SecKeychainAttributeList
*attrListPtr
;
1790 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1791 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1793 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeychainAttributes
, sizeof(SecKeychainAttribute
));
1794 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1797 // [0] get the serverName string
1798 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrServer
, (const void **)&value
) ) {
1799 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecServerItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1800 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1802 ++attrListPtr
->count
;
1805 // [1] get the securityDomain string
1806 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrSecurityDomain
, (const void **)&value
) ) {
1807 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecSecurityDomainItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1808 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1810 ++attrListPtr
->count
;
1813 // [2] get the accountName string
1814 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAccount
, (const void **)&value
) ) {
1815 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecAccountItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1816 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1818 ++attrListPtr
->count
;
1821 // [3] get the path string
1822 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPath
, (const void **)&value
) ) {
1823 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecPathItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1824 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1826 ++attrListPtr
->count
;
1829 // [4] get the port number
1830 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPort
, (const void **)&value
) ) {
1831 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt16
));
1832 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1834 CFTypeRef num
= copyNumber(value
);
1835 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1836 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecPortItemAttr
;
1837 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt16
);
1838 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt16Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1841 ++attrListPtr
->count
;
1844 // [5] get the protocol
1845 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrProtocol
, (const void **)&value
) ) {
1846 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecProtocolType
));
1847 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_protocol_failed
, status
= errSecBufferTooSmall
);
1849 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecProtocolItemAttr
;
1850 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecProtocolType
);
1851 *(SecProtocolType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecProtocolTypeForSecAttrProtocol(value
);
1853 ++attrListPtr
->count
;
1856 // [6] get the authenticationType
1857 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAuthenticationType
, (const void **)&value
) ) {
1858 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecAuthenticationType
));
1859 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_authenticationType_failed
, status
= errSecBufferTooSmall
);
1861 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecAuthenticationTypeItemAttr
;
1862 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecAuthenticationType
);
1863 *(SecAuthenticationType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecAuthenticationTypeForSecAttrAuthenticationType(value
);
1865 ++attrListPtr
->count
;
1868 // [7] get the comment string
1869 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrComment
, (const void **)&value
) ) {
1870 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecCommentItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1871 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1873 ++attrListPtr
->count
;
1876 // [8] get the description string
1877 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrDescription
, (const void **)&value
) ) {
1878 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecDescriptionItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1879 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1881 ++attrListPtr
->count
;
1884 // [9] get the label string
1885 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) ) {
1886 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecLabelItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1887 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1889 ++attrListPtr
->count
;
1892 // [10] get the creator code
1893 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCreator
, (const void **)&value
) ) {
1894 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1895 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1897 CFTypeRef num
= copyNumber(value
);
1898 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1899 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecCreatorItemAttr
;
1900 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1901 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1904 ++attrListPtr
->count
;
1907 // [11] get the type code
1908 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrType
, (const void **)&value
) ) {
1909 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1910 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1912 CFTypeRef num
= copyNumber(value
);
1913 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1914 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecTypeItemAttr
;
1915 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1916 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1919 ++attrListPtr
->count
;
1922 // [12] get the invisible flag
1923 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsInvisible
, (const void **)&value
) ) {
1924 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1925 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1927 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecInvisibleItemAttr
;
1928 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1929 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1931 ++attrListPtr
->count
;
1934 // [13] get the negative flag
1935 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsNegative
, (const void **)&value
) ) {
1936 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1937 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1939 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecNegativeItemAttr
;
1940 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1941 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1943 ++attrListPtr
->count
;
1946 // return the pointer to the attrList
1947 *attrList
= attrListPtr
;
1949 return ( errSecSuccess
);
1953 malloc_authenticationType_failed
:
1954 malloc_protocol_failed
:
1956 CFStringCreateAttribute_failed
:
1957 malloc_attrPtr_failed
:
1959 // free any attributes
1960 _FreeAttrList(attrListPtr
);
1962 calloc_attrListPtr_failed
:
1964 return ( errSecBufferTooSmall
);
1969 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
1970 * from the attribute key/values in attrDictionary for the specified item class.
1972 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1973 * must be freed by the caller with _FreeAttrList()
1976 _CreateSecKeychainAttributeListFromDictionary(
1977 CFDictionaryRef attrDictionary
,
1978 SecItemClass itemClass
,
1979 SecKeychainAttributeList
**attrList
)
1983 case kSecInternetPasswordItemClass
:
1984 return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1986 case kSecGenericPasswordItemClass
:
1987 return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1989 case kSecCertificateItemClass
:
1990 return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary
, attrList
);
1992 case kSecPublicKeyItemClass
:
1993 case kSecPrivateKeyItemClass
:
1994 case kSecSymmetricKeyItemClass
:
1995 return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary
, attrList
);
2005 * _AppNameFromSecTrustedApplication attempts to pull the name of the
2006 * application/tool from the SecTrustedApplicationRef.
2009 _AppNameFromSecTrustedApplication(
2010 CFAllocatorRef alloc
,
2011 SecTrustedApplicationRef appRef
)
2015 CFDataRef appDataRef
;
2019 // get the data for item's application/tool
2020 status
= SecTrustedApplicationCopyData(appRef
, &appDataRef
);
2021 if ( status
== errSecSuccess
) {
2024 // convert it to a CFString potentially containing the path
2025 path
= CFStringCreateWithCString(NULL
, (char *)CFDataGetBytePtrVoid(appDataRef
), kCFStringEncodingUTF8
);
2026 if ( path
!= NULL
) {
2027 // the path has to start with a "/" and cannot contain "://"
2028 if ( CFStringHasPrefix(path
, CFSTR("/")) && (CFStringFind(path
, CFSTR("://"), 0).location
== kCFNotFound
) ) {
2029 CFRange nameRange
, compRg
;
2031 nameRange
= CFRangeMake(0, CFStringGetLength(path
));
2033 // remove the trailing slashes (if any)
2034 while ( (nameRange
.length
> 0) && (CFStringGetCharacterAtIndex(path
, nameRange
.length
- 1) == '/') ) {
2035 nameRange
.length
--;
2038 if ( nameRange
.length
> 0 ) {
2039 // find last slash and adjust nameRange to be everything after it
2040 if ( CFStringFindWithOptions(path
, CFSTR("/"), nameRange
, kCFCompareBackwards
, &compRg
) ) {
2041 nameRange
.length
= nameRange
.location
+ nameRange
.length
- (compRg
.location
+ 1);
2042 nameRange
.location
= compRg
.location
+ 1;
2045 result
= CFStringCreateWithSubstring(alloc
, path
, nameRange
);
2050 CFRelease(appDataRef
);
2056 /* (This function really belongs in SecIdentity.cpp!)
2058 * Returns the public key item corresponding to the identity, if it exists in
2059 * the same keychain as the private key. Note that the public key might not
2060 * exist in the same keychain (e.g. if the identity was imported via PKCS12),
2061 * in which case it will not be found.
2064 _SecIdentityCopyPublicKey(
2065 SecIdentityRef identityRef
,
2066 SecKeyRef
*publicKeyRef
)
2070 SecKeychainAttribute attr
= { kSecKeyLabel
, 0, NULL
};
2071 SecKeychainAttributeList attrList
= { 1, &attr
};
2072 SecKeychainAttributeList
*keyAttrList
= NULL
;
2073 SecKeychainAttributeInfo
*info
= NULL
;
2074 SecKeychainSearchRef search
= NULL
;
2075 SecKeychainRef keychain
= NULL
;
2076 SecKeychainItemRef privateKey
= NULL
;
2077 SecKeychainItemRef publicKey
= NULL
;
2079 status
= SecIdentityCopyPrivateKey(identityRef
, (SecKeyRef
*)&privateKey
);
2081 goto error_exit
; // identity must have a private key
2083 status
= SecKeychainItemCopyKeychain(privateKey
, &keychain
);
2085 goto error_exit
; // private key must have a keychain, so we can get the attribute info for it
2087 status
= SecKeychainAttributeInfoForItemID(keychain
, kSecPrivateKeyItemClass
, &info
);
2089 goto error_exit
; // unable to get the attribute info (i.e. database schema) for private keys
2091 status
= SecKeychainItemCopyAttributesAndData(privateKey
, info
, NULL
, &keyAttrList
, NULL
, NULL
);
2093 goto error_exit
; // unable to get the key label attribute for the private key
2096 // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
2097 for (count
= 0; count
< keyAttrList
->count
; count
++) {
2098 if (keyAttrList
->attr
[count
].tag
== kSecKeyLabel
) {
2099 attr
.length
= keyAttrList
->attr
[count
].length
;
2100 attr
.data
= keyAttrList
->attr
[count
].data
;
2104 if (!attr
.length
|| !attr
.data
) {
2105 status
= errSecNoSuchAttr
;
2106 goto error_exit
; // the private key didn't have the hash of the public key in its kSecKeyLabel
2108 status
= SecKeychainSearchCreateFromAttributes(keychain
, kSecPublicKeyItemClass
, &attrList
, &search
);
2110 goto error_exit
; // unable to create the search reference
2112 status
= SecKeychainSearchCopyNext(search
, &publicKey
);
2114 goto error_exit
; // unable to find the public key
2118 *publicKeyRef
= (SecKeyRef
)publicKey
;
2120 CFRelease(publicKey
);
2123 if (status
!= errSecSuccess
) {
2125 *publicKeyRef
= NULL
;
2127 CFRelease(publicKey
);
2133 SecKeychainItemFreeAttributesAndData(keyAttrList
, NULL
);
2136 SecKeychainFreeAttributeInfo(info
);
2139 CFRelease(keychain
);
2142 CFRelease(privateKey
);
2149 * Deletes a keychain item if the current application/tool is the only application/tool
2150 * with decrypt access to that keychain item. If more than one application/tool
2151 * has decrypt access to the keychain item, the item is left on the keychain.
2153 * TBD: If more than one app/tool has access to the keychain item, we should remove
2154 * the current app/tool's decrypt access. There's no easy way to do that with
2155 * current keychain APIs without bringing up the security UI.
2158 _SafeSecKeychainItemDelete(
2159 SecKeychainItemRef itemRef
)
2162 SecAccessRef access
= NULL
;
2163 CFArrayRef aclList
= NULL
;
2164 SecACLRef acl
= NULL
;
2165 CFArrayRef appList
= NULL
;
2166 CFStringRef description
= NULL
;
2167 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
;
2168 CFIndex idx
, count
= 0;
2169 SecTrustedApplicationRef currentAppRef
= NULL
;
2170 CFStringRef itemAppName
= NULL
, currentAppName
= NULL
;
2172 SecItemClass itemClass
= (SecItemClass
)0;
2173 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2174 if (!(itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2175 // only perform the access control safety check on deletion of password credentials;
2176 // if the item is of some other type, delete it normally.
2177 return SecKeychainItemDelete(itemRef
);
2180 // skip access control checking for web form passwords: <rdar://10957301>
2181 // This permits Safari to manage the removal of all web form passwords,
2182 // regardless of whether they are shared by multiple applications.
2183 if (itemClass
== kSecInternetPasswordItemClass
) {
2184 UInt32 tags
[1] = { kSecAuthenticationTypeItemAttr
};
2185 SecKeychainAttributeInfo attrInfo
= { 1, tags
, NULL
};
2186 SecKeychainAttributeList
*attrs
= NULL
;
2187 status
= SecKeychainItemCopyAttributesAndData(itemRef
, &attrInfo
, NULL
, &attrs
, NULL
, NULL
);
2188 if (!status
&& attrs
) {
2189 bool webFormPassword
= (attrs
->attr
[0].length
== 4 && (!memcmp(attrs
->attr
[0].data
, "form", 4)));
2190 SecKeychainItemFreeAttributesAndData(attrs
, NULL
);
2191 if (webFormPassword
) {
2192 return SecKeychainItemDelete(itemRef
);
2197 // copy the access of the keychain item
2198 status
= SecKeychainItemCopyAccess(itemRef
, &access
);
2199 require_noerr(status
, finish
);
2200 require_quiet(access
!= NULL
, finish
);
2202 // copy the decrypt access control lists -- this is what has access to the keychain item
2203 status
= SecAccessCopySelectedACLList(access
, CSSM_ACL_AUTHORIZATION_DECRYPT
, &aclList
);
2204 require_noerr(status
, finish
);
2205 require_quiet(aclList
!= NULL
, finish
);
2207 // get the access control list
2208 acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, 0);
2209 require_quiet(acl
!= NULL
, finish
);
2211 // copy the application list, description, and CSSM prompt selector for a given access control list entry
2212 status
= SecACLCopySimpleContents(acl
, &appList
, &description
, &promptSelector
);
2213 require_noerr(status
, finish
);
2214 require_quiet(appList
!= NULL
, finish
);
2216 // does the calling application/tool have decrypt access to this item?
2217 count
= CFArrayGetCount(appList
);
2218 for ( idx
= 0; idx
< count
; idx
++ ) {
2219 // get SecTrustedApplicationRef for this entry
2220 SecTrustedApplicationRef itemAppRef
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(appList
, idx
);
2221 require_quiet(itemAppRef
!= NULL
, finish
);
2223 // copy the name out
2224 CFReleaseSafe(itemAppName
);
2225 itemAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), itemAppRef
);
2226 if (itemAppName
== NULL
) {
2228 * If there is no app name, it's probably because it's not an appname
2229 * in the ACE but an entitlement/info.plist based rule instead;
2230 * just let the caller have it. */
2235 // create SecTrustedApplicationRef for current application/tool
2236 CFReleaseSafe(currentAppRef
);
2237 status
= SecTrustedApplicationCreateFromPath(NULL
, ¤tAppRef
);
2238 require_noerr(status
, finish
);
2239 require_quiet(currentAppRef
!= NULL
, finish
);
2241 // copy the name out
2242 CFReleaseSafe(currentAppName
);
2243 currentAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), currentAppRef
);
2244 require_quiet(currentAppName
!= NULL
, finish
);
2246 // compare the names to see if we own the decrypt access
2247 // TBD: validation of membership in an application group
2248 if ( CFStringCompare(currentAppName
, itemAppName
, 0) == kCFCompareEqualTo
) {
2256 CFReleaseSafe(currentAppName
);
2257 CFReleaseSafe(itemAppName
);
2258 CFReleaseSafe(currentAppRef
);
2259 CFReleaseSafe(description
);
2260 CFReleaseSafe(appList
);
2261 CFReleaseSafe(aclList
);
2262 CFReleaseSafe(access
);
2264 if ((count
== 0) || (status
== errSecVerifyFailed
)) {
2265 // no "owners" remain in the ACL list (or unable to get ACL)
2266 status
= SecKeychainItemDelete(itemRef
);
2268 // caller is not the "owner" of the item
2269 status
= errSecInvalidOwnerEdit
;
2276 _ReplaceKeychainItem(
2277 SecKeychainItemRef itemToUpdate
,
2278 SecKeychainAttributeList
*changeAttrList
,
2283 SecItemClass itemClass
;
2284 SecKeychainAttributeInfo
*info
= NULL
;
2285 SecKeychainAttributeList
*attrList
= NULL
;
2286 SecKeychainAttributeList newAttrList
= { 0, NULL
};
2287 SecKeychainRef keychain
= NULL
;
2288 SecKeychainItemRef newItem
= NULL
;
2290 int priority
= LOG_DEBUG
;
2291 const char *format
= "ReplaceKeychainItem (%d) error %d";
2293 // get existing item's keychain
2294 status
= SecKeychainItemCopyKeychain(itemToUpdate
, &keychain
);
2295 if (status
) { secitemlog(priority
, format
, 1, (int)status
); }
2296 require_noerr(status
, replace_failed
);
2298 // get attribute info (i.e. database schema) for the item class
2299 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2300 if (status
) { secitemlog(priority
, format
, 2, (int)status
); }
2301 require_noerr(status
, replace_failed
);
2305 case kSecInternetPasswordItemClass
:
2306 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
2308 case kSecGenericPasswordItemClass
:
2309 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
2315 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
2316 if (status
) { secitemlog(priority
, format
, 3, (int)status
); }
2318 // get item's existing attributes (but not data!)
2319 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, info
, &itemClass
, &attrList
, NULL
, NULL
);
2320 if (status
) { secitemlog(priority
, format
, 4, (int)status
); }
2321 require(attrList
!= NULL
, replace_failed
);
2323 // move aside the item by changing a primary attribute
2324 // (currently only for passwords)
2325 if (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
) {
2326 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
2327 CFStringRef uuidStr
= (uuid
) ? CFUUIDCreateString(kCFAllocatorDefault
, uuid
) : CFSTR("MOVED");
2328 CFReleaseSafe(uuid
);
2330 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(uuidStr
), kCFStringEncodingUTF8
) + 1;
2331 char* buffer
= (char*) malloc(maxLength
);
2333 if (CFStringGetCString(uuidStr
, buffer
, maxLength
, kCFStringEncodingUTF8
)) {
2334 UInt32 length
= (UInt32
)strlen(buffer
);
2335 SecKeychainAttribute attrs
[] = { { kSecAccountItemAttr
, length
, (char*)buffer
}, };
2336 SecKeychainAttributeList updateAttrList
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
2337 status
= SecKeychainItemModifyAttributesAndData(itemToUpdate
, &updateAttrList
, 0, NULL
);
2338 if (status
) { secitemlog(priority
, format
, 5, (int)status
); }
2339 if (status
== errSecVerifyFailed
) {
2340 // still unable to change attrs? delete unconditionally here
2341 status
= SecKeychainItemDelete(itemToUpdate
);
2342 if (status
) { secitemlog(priority
, format
, 6, (int)status
); }
2347 CFReleaseSafe(uuidStr
);
2350 require_noerr(status
, replace_failed
);
2352 // make attribute list for new item (the data is still owned by attrList)
2353 newAttrList
.count
= attrList
->count
;
2354 newAttrList
.attr
= (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * attrList
->count
);
2356 for (i
=0, newCount
=0; i
< attrList
->count
; i
++) {
2357 if (attrList
->attr
[i
].length
> 0) {
2358 newAttrList
.attr
[newCount
++] = attrList
->attr
[i
];
2360 // debugging code to log item attributes
2361 SecKeychainAttrType tag
= attrList
->attr
[i
].tag
;
2362 SecKeychainAttrType htag
=(SecKeychainAttrType
)OSSwapConstInt32(tag
);
2363 char tmp
[sizeof(SecKeychainAttrType
) + 1];
2364 char tmpdata
[attrList
->attr
[i
].length
+ 1];
2365 memcpy(tmp
, &htag
, sizeof(SecKeychainAttrType
));
2366 tmp
[sizeof(SecKeychainAttrType
)]=0;
2367 memcpy(tmpdata
, attrList
->attr
[i
].data
, attrList
->attr
[i
].length
);
2368 tmpdata
[attrList
->attr
[i
].length
]=0;
2369 secitemlog(priority
, "item attr '%s' = %d bytes: \"%s\"",
2370 tmp
, (int)attrList
->attr
[i
].length
, tmpdata
);
2374 newAttrList
.count
= newCount
;
2376 // create new item in the same keychain
2377 status
= SecKeychainItemCreateFromContent(itemClass
, &newAttrList
,
2378 (UInt32
)((itemData
) ? CFDataGetLength(itemData
) : 0),
2379 (const void *)((itemData
) ? CFDataGetBytePtr(itemData
) : NULL
),
2380 keychain
, NULL
, &newItem
);
2381 if (status
) { secitemlog(priority
, format
, 7, (int)status
); }
2382 require_noerr(status
, replace_failed
);
2384 // delete the old item unconditionally once new item exists
2385 status
= SecKeychainItemDelete(itemToUpdate
);
2387 // update the new item with changed attributes, if any
2388 status
= (changeAttrList
) ? SecKeychainItemModifyContent(newItem
, changeAttrList
, 0, NULL
) : errSecSuccess
;
2389 if (status
) { secitemlog(priority
, format
, 8, (int)status
); }
2390 if (status
== errSecSuccess
) {
2391 // say the item already exists, because it does now. <rdar://19063674>
2392 status
= errSecDuplicateItem
;
2396 if (newAttrList
.attr
) {
2397 free(newAttrList
.attr
);
2400 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
2403 SecKeychainFreeAttributeInfo(info
);
2405 CFReleaseSafe(newItem
);
2406 CFReleaseSafe(keychain
);
2412 _UpdateKeychainItem(CFTypeRef item
, CFDictionaryRef changedAttributes
)
2414 // This function updates a single keychain item, which may be specified as
2415 // a reference, persistent reference or attribute dictionary, with the
2416 // attributes provided.
2418 OSStatus status
= errSecSuccess
;
2423 SecItemClass itemClass
;
2424 SecAccessRef access
= NULL
;
2425 SecKeychainAttributeList
*changeAttrList
= NULL
;
2426 SecKeychainItemRef itemToUpdate
= NULL
;
2427 CFDataRef theData
= NULL
;
2428 CFTypeID itemType
= CFGetTypeID(item
);
2430 // validate input item (must be convertible to a SecKeychainItemRef)
2431 if (SecKeychainItemGetTypeID() == itemType
||
2432 SecCertificateGetTypeID() == itemType
||
2433 SecKeyGetTypeID() == itemType
) {
2434 // item is already a reference, retain it
2435 itemToUpdate
= (SecKeychainItemRef
) CFRetain(item
);
2437 else if (CFDataGetTypeID() == itemType
) {
2438 // item is a persistent reference, must convert it
2439 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToUpdate
);
2441 else if (CFDictionaryGetTypeID() == itemType
) {
2442 // item is a dictionary
2443 CFTypeRef value
= NULL
;
2444 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2445 // kSecValueRef value is a SecKeychainItemRef, retain it
2446 itemToUpdate
= (SecKeychainItemRef
) CFRetain(value
);
2448 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2449 // kSecValuePersistentRef value is a persistent reference, must convert it
2450 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToUpdate
);
2453 else if (SecIdentityGetTypeID() == itemType
) {
2454 // item is a certificate + private key; since we can't really change the
2455 // certificate's attributes, assume we want to update the private key
2456 status
= SecIdentityCopyPrivateKey((SecIdentityRef
)item
, (SecKeyRef
*)&itemToUpdate
);
2458 require_action(itemToUpdate
!= NULL
, update_failed
, status
= errSecInvalidItemRef
);
2459 require_noerr(status
, update_failed
);
2461 status
= SecKeychainItemCopyContent(itemToUpdate
, &itemClass
, NULL
, NULL
, NULL
);
2462 require_noerr(status
, update_failed
);
2464 // build changeAttrList from changedAttributes dictionary
2467 case kSecInternetPasswordItemClass
:
2469 status
= _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2470 require_noerr(status
, update_failed
);
2474 case kSecGenericPasswordItemClass
:
2476 status
= _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2477 require_noerr(status
, update_failed
);
2481 case kSecCertificateItemClass
:
2483 status
= _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2484 require_noerr(status
, update_failed
);
2488 case kSecPublicKeyItemClass
:
2489 case kSecPrivateKeyItemClass
:
2490 case kSecSymmetricKeyItemClass
:
2492 status
= _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2493 require_noerr(status
, update_failed
);
2498 // (if the caller is not updating the password, this value will be NULL)
2499 theData
= (CFDataRef
)CFDictionaryGetValue(changedAttributes
, kSecValueData
);
2500 if (theData
!= NULL
) {
2501 require_action(CFDataGetTypeID() == CFGetTypeID(theData
), update_failed
, status
= errSecParam
);
2504 status
= SecKeychainItemModifyContent(itemToUpdate
,
2505 (changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2506 (theData
!= NULL
) ? (UInt32
)CFDataGetLength(theData
) : 0,
2507 (theData
!= NULL
) ? CFDataGetBytePtrVoid(theData
) : NULL
);
2508 require_noerr(status
, update_failed
);
2510 // one more thing... update access?
2511 if (CFDictionaryGetValueIfPresent(changedAttributes
, kSecAttrAccess
, (const void **)&access
)) {
2512 status
= SecKeychainItemSetAccess(itemToUpdate
, access
);
2516 if (status
== errSecVerifyFailed
&&
2517 (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2518 // if we got a cryptographic failure updating a password item, it needs to be replaced
2519 status
= _ReplaceKeychainItem(itemToUpdate
,
2520 (changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2524 CFRelease(itemToUpdate
);
2525 _FreeAttrList(changeAttrList
);
2530 _DeleteKeychainItem(CFTypeRef item
)
2532 // This function deletes a single keychain item, which may be specified as
2533 // a reference, persistent reference or attribute dictionary. It will not
2534 // delete non-keychain items or aggregate items (such as a SecIdentityRef);
2535 // it is assumed that the caller will pass identity components separately.
2537 OSStatus status
= errSecSuccess
;
2542 SecKeychainItemRef itemToDelete
= NULL
;
2543 CFTypeID itemType
= CFGetTypeID(item
);
2544 if (SecKeychainItemGetTypeID() == itemType
||
2545 SecCertificateGetTypeID() == itemType
||
2546 SecKeyGetTypeID() == itemType
) {
2547 // item is already a reference, retain it
2548 itemToDelete
= (SecKeychainItemRef
) CFRetain(item
);
2550 else if (CFDataGetTypeID() == itemType
) {
2551 // item is a persistent reference, must convert it
2552 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToDelete
);
2554 else if (CFDictionaryGetTypeID() == itemType
) {
2555 // item is a dictionary
2556 CFTypeRef value
= NULL
;
2557 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2558 // kSecValueRef value is a SecKeychainItemRef, retain it
2559 itemToDelete
= (SecKeychainItemRef
) CFRetain(value
);
2561 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2562 // kSecValuePersistentRef value is a persistent reference, must convert it
2563 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToDelete
);
2569 status
= _SafeSecKeychainItemDelete(itemToDelete
);
2571 CFRelease(itemToDelete
);
2578 _DeleteIdentity(SecIdentityRef identity
)
2580 OSStatus status
, result
= errSecSuccess
;
2581 SecKeyRef privateKey
= NULL
;
2582 SecCertificateRef certificate
= NULL
;
2584 status
= SecIdentityCopyPrivateKey(identity
, &privateKey
);
2586 SecKeyRef publicKey
= NULL
;
2587 status
= _SecIdentityCopyPublicKey(identity
, &publicKey
);
2589 status
= _DeleteKeychainItem(publicKey
);
2590 CFRelease(publicKey
);
2592 status
= _DeleteKeychainItem(privateKey
);
2595 if (privateKey
) CFRelease(privateKey
);
2596 if (status
) result
= status
;
2598 status
= SecIdentityCopyCertificate(identity
, &certificate
);
2600 status
= _DeleteKeychainItem(certificate
);
2603 if (certificate
) CFRelease(certificate
);
2604 if (status
) result
= status
;
2610 _UpdateAggregateStatus(OSStatus newStatus
, OSStatus curStatus
, OSStatus baseStatus
)
2612 // This function is used when atomically processing multiple items,
2613 // where an overall error result must be returned for the entire operation.
2614 // When newStatus is something other than errSecSuccess, we want to keep the "most
2615 // interesting" status (which usually will be newStatus, unless curStatus is
2616 // already set; in that case, newStatus can trump curStatus only by being
2617 // something different than baseStatus.)
2619 OSStatus result
= curStatus
;
2621 if (newStatus
!= errSecSuccess
) {
2623 if (curStatus
!= errSecSuccess
) {
2624 result
= (newStatus
!= baseStatus
) ? newStatus
: curStatus
;
2631 _AddDictValueToOtherDict(const void *key
, const void *value
, void *context
)
2633 // CFDictionaryApplierFunction
2634 // This function just takes the given key/value pair,
2635 // and adds it to another dictionary supplied in the context argument.
2637 CFMutableDictionaryRef dict
= *((CFMutableDictionaryRef
*) context
);
2639 CFDictionaryAddValue(dict
, key
, value
);
2643 static CFStringCompareFlags
2644 _StringCompareFlagsFromQuery(CFDictionaryRef query
)
2647 CFStringCompareFlags flags
= 0;
2648 if (!query
) return flags
;
2650 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&value
) ||
2651 CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2652 flags
|= kCFCompareAnchored
;
2654 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2655 flags
|= kCFCompareBackwards
;
2657 if (CFDictionaryGetValueIfPresent(query
, kSecMatchCaseInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2658 flags
|= kCFCompareCaseInsensitive
;
2660 if (CFDictionaryGetValueIfPresent(query
, kSecMatchDiacriticInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2661 flags
|= kCFCompareDiacriticInsensitive
;
2663 if (CFDictionaryGetValueIfPresent(query
, kSecMatchWidthInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2664 flags
|= kCFCompareWidthInsensitive
;
2670 _CssmKeyUsageFromQuery(CFDictionaryRef query
)
2673 uint32 keyUsage
= 0;
2674 if (!query
) return keyUsage
;
2676 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanEncrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2677 keyUsage
|= CSSM_KEYUSE_ENCRYPT
;
2679 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDecrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2680 keyUsage
|= CSSM_KEYUSE_DECRYPT
;
2682 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanSign
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2683 keyUsage
|= CSSM_KEYUSE_SIGN
;
2685 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanVerify
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2686 keyUsage
|= CSSM_KEYUSE_VERIFY
;
2688 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanWrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2689 keyUsage
|= CSSM_KEYUSE_WRAP
;
2691 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanUnwrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2692 keyUsage
|= CSSM_KEYUSE_UNWRAP
;
2694 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDerive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2695 keyUsage
|= CSSM_KEYUSE_DERIVE
;
2701 _ConvertItemClass(const void* item
, const void* keyClass
, Boolean
*isIdentity
)
2703 SecItemClass itemClass
= (SecItemClass
) 0;
2704 if (isIdentity
) *isIdentity
= false;
2706 if (CFEqual(item
, kSecClassGenericPassword
)) {
2707 itemClass
= kSecGenericPasswordItemClass
;
2709 else if (CFEqual(item
, kSecClassInternetPassword
)) {
2710 itemClass
= kSecInternetPasswordItemClass
;
2712 else if (CFEqual(item
, kSecClassCertificate
)) {
2713 itemClass
= kSecCertificateItemClass
;
2715 else if (CFEqual(item
, kSecClassIdentity
)) {
2716 // will perform a certificate lookup
2717 itemClass
= kSecCertificateItemClass
;
2718 if (isIdentity
) *isIdentity
= true;
2720 else if (CFEqual(item
, kSecClassKey
)) {
2721 // examine second parameter to determine type of key
2722 if (!keyClass
|| CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
2723 itemClass
= kSecSymmetricKeyItemClass
;
2725 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPublic
)) {
2726 itemClass
= kSecPublicKeyItemClass
;
2728 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
2729 itemClass
= kSecPrivateKeyItemClass
;
2737 _ItemClassFromItemList(CFArrayRef itemList
)
2739 // Given a list of items (standard or persistent references),
2740 // determine whether they all have the same item class. Returns
2741 // the item class, or 0 if multiple classes in list.
2742 SecItemClass result
= 0;
2743 CFIndex index
, count
= (itemList
) ? CFArrayGetCount(itemList
) : 0;
2744 for (index
=0; index
< count
; index
++) {
2745 CFTypeRef item
= (CFTypeRef
) CFArrayGetValueAtIndex(itemList
, index
);
2747 SecKeychainItemRef itemRef
= NULL
;
2749 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
2750 // persistent reference, resolve first
2751 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemRef
);
2754 itemRef
= (SecKeychainItemRef
) CFRetain(item
);
2757 SecItemClass itemClass
= 0;
2758 CFTypeID itemTypeID
= CFGetTypeID(itemRef
);
2759 if (itemTypeID
== SecIdentityGetTypeID() || itemTypeID
== SecCertificateGetTypeID()) {
2760 // Identities and certificates have the same underlying item class
2761 itemClass
= kSecCertificateItemClass
;
2763 else if (itemTypeID
== SecKeychainItemGetTypeID()) {
2764 // Reference to item in a keychain
2765 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2767 else if (itemTypeID
== SecKeyGetTypeID()) {
2768 // SecKey that isn't stored in a keychain
2769 // %%% will need to change this code when SecKey is no longer CSSM-based %%%
2770 const CSSM_KEY
*cssmKey
;
2771 status
= SecKeyGetCSSMKey((SecKeyRef
)itemRef
, &cssmKey
);
2772 if (status
== errSecSuccess
) {
2773 if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PUBLIC_KEY
)
2774 itemClass
= kSecPublicKeyItemClass
;
2775 else if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PRIVATE_KEY
)
2776 itemClass
= kSecPrivateKeyItemClass
;
2778 itemClass
= kSecSymmetricKeyItemClass
;
2782 if (itemClass
!= 0) {
2783 if (result
!= 0 && result
!= itemClass
) {
2784 return 0; // different item classes in list; bail out
2794 // SecItemParams contains a validated set of input parameters, as well as a
2795 // search reference and attribute list built from those parameters. It is
2796 // designed to be allocated with _CreateSecItemParamsFromDictionary, and
2797 // freed with _FreeSecItemParams.
2799 struct SecItemParams
{
2800 CFDictionaryRef query
; // caller-supplied query
2801 int numResultTypes
; // number of result types requested
2802 int maxMatches
; // max number of matches to return
2803 uint32 keyUsage
; // key usage(s) requested
2804 Boolean returningAttributes
; // true if returning attributes dictionary
2805 Boolean returningData
; // true if returning item's data
2806 Boolean returningRef
; // true if returning item reference
2807 Boolean returningPersistentRef
; // true if returing a persistent reference
2808 Boolean returnAllMatches
; // true if we should return all matches
2809 Boolean returnIdentity
; // true if we are returning a SecIdentityRef
2810 Boolean trustedOnly
; // true if we only return trusted certs
2811 Boolean issuerAndSNToMatch
; // true if both issuer and SN were provided
2812 SecItemClass itemClass
; // item class for this query
2813 SecPolicyRef policy
; // value for kSecMatchPolicy (may be NULL)
2814 SecKeychainRef keychain
; // value for kSecUseKeychain (may be NULL)
2815 CFArrayRef useItems
; // value for kSecUseItemList (may be NULL)
2816 CFArrayRef itemList
; // value for kSecMatchItemList (may be NULL)
2817 CFTypeRef searchList
; // value for kSecMatchSearchList (may be NULL)
2818 CFTypeRef matchLimit
; // value for kSecMatchLimit (may be NULL)
2819 CFTypeRef emailAddrToMatch
; // value for kSecMatchEmailAddressIfPresent (may be NULL)
2820 CFTypeRef validOnDate
; // value for kSecMatchValidOnDate (may be NULL)
2821 CFTypeRef keyClass
; // value for kSecAttrKeyClass (may be NULL)
2822 CFTypeRef service
; // value for kSecAttrService (may be NULL)
2823 CFTypeRef issuer
; // value for kSecAttrIssuer (may be NULL)
2824 CFTypeRef serialNumber
; // value for kSecAttrSerialNumber (may be NULL)
2825 CFTypeRef search
; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2826 CFTypeRef assumedKeyClass
; // if no kSecAttrKeyClass provided, holds the current class we're searching for
2827 CFIndex itemListIndex
; // if no search reference but we have itemList, holds index of next item to return
2828 SecKeychainAttributeList
*attrList
; // attribute list for this query
2829 SecAccessRef access
; // access reference (for SecItemAdd only, not used to find items)
2830 CFDataRef itemData
; // item data (for SecItemAdd only, not used to find items)
2831 CFTypeRef itemRef
; // item reference (to find, add, update or delete, depending on context)
2832 SecIdentityRef identityRef
; // identity reference (input as kSecValueRef)
2833 CFDataRef itemPersistentRef
; // item persistent reference (to find, add, update or delete, depending on context)
2834 Boolean isPCSItem
; // true if this query is for a Protected Cloud Storage item
2838 _ValidateDictionaryEntry(CFDictionaryRef dict
, CFTypeRef key
, const void **value
, CFTypeID expectedTypeID
, CFTypeID altTypeID
)
2840 if (!dict
|| !key
|| !value
|| !expectedTypeID
)
2843 if (!CFDictionaryGetValueIfPresent(dict
, key
, value
)) {
2844 // value was not provided for this key (not an error!)
2847 else if (!(*value
)) {
2848 // provided value is NULL (also not an error!)
2849 return errSecSuccess
;
2852 CFTypeID actualTypeID
= CFGetTypeID(*value
);
2853 if (!((expectedTypeID
== actualTypeID
) || (altTypeID
&& altTypeID
== actualTypeID
))) {
2854 // provided value does not have the expected (or alternate) CF type ID
2855 if ((expectedTypeID
== SecKeychainItemGetTypeID()) &&
2856 (actualTypeID
== SecKeyGetTypeID() || actualTypeID
== SecCertificateGetTypeID())) {
2857 // provided value is a "floating" reference which is not yet in a keychain
2859 return errSecSuccess
;
2861 return errSecItemInvalidValue
;
2864 // provided value is OK; retain it
2868 return errSecSuccess
;
2872 _EnsureUserDefaultKeychainIsSearched(SecItemParams
*itemParams
)
2875 CFArrayRef tmpList
= (CFArrayRef
) itemParams
->searchList
;
2877 // search list exists; make it mutable
2878 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2881 // no search list; start with default list
2882 status
= SecKeychainCopySearchList(&tmpList
);
2883 if (!status
&& tmpList
) {
2884 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2888 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2892 SecKeychainRef userKeychain
= NULL
;
2893 status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2894 if (!status
&& userKeychain
) {
2895 if (!CFArrayContainsValue((CFArrayRef
)itemParams
->searchList
,
2896 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)itemParams
->searchList
)), userKeychain
)) {
2897 // user's default keychain isn't currently in the search list, so append it
2898 CFArrayAppendValue((CFMutableArrayRef
)itemParams
->searchList
, userKeychain
);
2900 CFRelease(userKeychain
);
2905 _EnsureUserDefaultKeychainIsTargeted(SecItemParams
*itemParams
)
2907 if (itemParams
->keychain
) {
2908 return; // keychain is already explicitly specified, assume it's correct
2910 SecKeychainRef userKeychain
= NULL
;
2911 OSStatus status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2912 if (!status
&& userKeychain
) {
2913 itemParams
->keychain
= userKeychain
;
2918 _FreeSecItemParams(SecItemParams
*itemParams
)
2923 if (itemParams
->query
) CFRelease(itemParams
->query
);
2924 if (itemParams
->policy
) CFRelease(itemParams
->policy
);
2925 if (itemParams
->keychain
) CFRelease(itemParams
->keychain
);
2926 if (itemParams
->useItems
) CFRelease(itemParams
->useItems
);
2927 if (itemParams
->itemList
) CFRelease(itemParams
->itemList
);
2928 if (itemParams
->searchList
) CFRelease(itemParams
->searchList
);
2929 if (itemParams
->matchLimit
) CFRelease(itemParams
->matchLimit
);
2930 if (itemParams
->emailAddrToMatch
) CFRelease(itemParams
->emailAddrToMatch
);
2931 if (itemParams
->validOnDate
) CFRelease(itemParams
->validOnDate
);
2932 if (itemParams
->keyClass
) CFRelease(itemParams
->keyClass
);
2933 if (itemParams
->service
) CFRelease(itemParams
->service
);
2934 if (itemParams
->issuer
) CFRelease(itemParams
->issuer
);
2935 if (itemParams
->serialNumber
) CFRelease(itemParams
->serialNumber
);
2936 if (itemParams
->search
) CFRelease(itemParams
->search
);
2937 if (itemParams
->access
) CFRelease(itemParams
->access
);
2938 if (itemParams
->itemData
) CFRelease(itemParams
->itemData
);
2939 if (itemParams
->itemRef
) CFRelease(itemParams
->itemRef
);
2940 if (itemParams
->identityRef
) CFRelease(itemParams
->identityRef
);
2941 if (itemParams
->itemPersistentRef
) CFRelease(itemParams
->itemPersistentRef
);
2943 _FreeAttrList(itemParams
->attrList
);
2948 static SecItemParams
*
2949 _CreateSecItemParamsFromDictionary(CFDictionaryRef dict
, OSStatus
*error
)
2952 CFTypeRef value
= NULL
;
2953 SecItemParams
*itemParams
= (SecItemParams
*)calloc(1, sizeof(struct SecItemParams
));
2955 require_action(itemParams
!= NULL
, error_exit
, status
= errSecAllocate
);
2956 require_action(dict
&& (CFDictionaryGetTypeID() == CFGetTypeID(dict
)), error_exit
, status
= errSecParam
);
2958 itemParams
->query
= (CFDictionaryRef
) CFRetain(dict
);
2960 // validate input search parameters
2961 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchPolicy
, (const void **)&itemParams
->policy
, SecPolicyGetTypeID(), NULL
), error_exit
);
2962 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchSearchList
, (const void **)&itemParams
->searchList
, CFArrayGetTypeID(), NULL
), error_exit
);
2963 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchItemList
, (const void **)&itemParams
->itemList
, CFArrayGetTypeID(), NULL
), error_exit
);
2964 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchEmailAddressIfPresent
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
2965 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchValidOnDate
, (const void **)&itemParams
->validOnDate
, CFDateGetTypeID(), CFNullGetTypeID()), error_exit
);
2966 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchLimit
, (const void **)&itemParams
->matchLimit
, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit
);
2968 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseItemList
, (const void **)&itemParams
->useItems
, CFArrayGetTypeID(), NULL
), error_exit
);
2969 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseKeychain
, (const void **)&itemParams
->keychain
, SecKeychainGetTypeID(), NULL
), error_exit
);
2971 // validate a subset of input attributes (used to create an appropriate search reference)
2972 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrIssuer
, (const void **)&itemParams
->issuer
, CFDataGetTypeID(), NULL
), error_exit
);
2973 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrSerialNumber
, (const void **)&itemParams
->serialNumber
, CFDataGetTypeID(), NULL
), error_exit
);
2974 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrService
, (const void **)&itemParams
->service
, CFStringGetTypeID(), NULL
), error_exit
);
2975 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrKeyClass
, (const void **)&itemParams
->keyClass
, CFStringGetTypeID(), NULL
), error_exit
);
2977 if (itemParams
->service
&& CFStringHasPrefix((CFStringRef
)itemParams
->service
, CFSTR("ProtectedCloudStorage"))) {
2978 itemParams
->isPCSItem
= true;
2979 if (!SecItemSynchronizable(dict
)) {
2980 _EnsureUserDefaultKeychainIsSearched(itemParams
); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
2981 _EnsureUserDefaultKeychainIsTargeted(itemParams
); // for SecItemAdd
2985 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
2986 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueData
, (const void **)&itemParams
->itemData
, CFDataGetTypeID(), CFStringGetTypeID()), error_exit
);
2987 if (itemParams
->itemData
&& CFGetTypeID(itemParams
->itemData
) == CFStringGetTypeID()) {
2988 /* If we got a string, convert it into a data object */
2989 CFStringRef string
= (CFStringRef
)itemParams
->itemData
;
2990 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(string
), kCFStringEncodingUTF8
) + 1;
2991 CFMutableDataRef data
= CFDataCreateMutable(NULL
, maxLength
);
2992 require_action(data
, error_exit
, status
= errSecAllocate
);
2994 CFDataSetLength(data
, maxLength
);
2996 if (!CFStringGetCString(string
, (char *)CFDataGetMutableBytePtr(data
), maxLength
, kCFStringEncodingUTF8
)) {
2998 status
= errSecAllocate
;
3002 CFDataSetLength(data
, strlen((const char *)CFDataGetBytePtr(data
))); /* dont include NUL in string */
3003 itemParams
->itemData
= data
;
3007 // validate item references
3008 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueRef
, (const void **)&itemParams
->itemRef
, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit
);
3009 if (itemParams
->itemRef
&& (CFGetTypeID(itemParams
->itemRef
) == SecIdentityGetTypeID())) {
3010 itemParams
->identityRef
= (SecIdentityRef
)itemParams
->itemRef
;
3011 itemParams
->itemRef
= NULL
;
3012 SecIdentityCopyCertificate(itemParams
->identityRef
, (SecCertificateRef
*)&itemParams
->itemRef
);
3014 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValuePersistentRef
, (const void **)&itemParams
->itemPersistentRef
, CFDataGetTypeID(), NULL
), error_exit
);
3015 if (itemParams
->itemRef
|| itemParams
->itemPersistentRef
) {
3016 // Caller is trying to add or find an item by reference.
3017 // The supported method for doing that is to provide a kSecUseItemList array
3018 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
3019 // so add the item reference to those arrays here.
3020 if (itemParams
->useItems
) {
3021 CFArrayRef tmpItems
= itemParams
->useItems
;
3022 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3023 CFRelease(tmpItems
);
3025 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3027 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemRef
);
3028 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemPersistentRef
);
3030 if (itemParams
->itemList
) {
3031 CFArrayRef tmpItems
= itemParams
->itemList
;
3032 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3033 CFRelease(tmpItems
);
3035 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3037 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemRef
);
3038 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemPersistentRef
);
3041 // must have an explicit item class, unless one of the following is true:
3042 // - we have an item list to add or search (kSecUseItemList)
3043 // - we have an item reference or persistent reference for the thing we want to look up
3044 // Note that both of these cases will set itemParams->useItems.
3045 // If we have an item list to match (kSecMatchItemList), that still requires an item class,
3046 // so we can perform a search and see if the results match items in the list.
3048 if (!CFDictionaryGetValueIfPresent(dict
, kSecClass
, (const void**) &value
) && !itemParams
->useItems
) {
3049 require_action(false, error_exit
, status
= errSecItemClassMissing
);
3052 itemParams
->itemClass
= _ConvertItemClass(value
, itemParams
->keyClass
, &itemParams
->returnIdentity
);
3053 if (itemParams
->itemClass
== kSecSymmetricKeyItemClass
&& !itemParams
->keyClass
) {
3054 // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
3055 itemParams
->itemClass
= kSecSymmetricKeyItemClass
;
3056 itemParams
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3058 require_action(!(itemParams
->itemClass
== 0 && !itemParams
->useItems
), error_exit
, status
= errSecItemClassMissing
);
3061 itemParams
->keyUsage
= _CssmKeyUsageFromQuery(dict
);
3062 itemParams
->trustedOnly
= CFDictionaryGetValueIfPresent(dict
, kSecMatchTrustedOnly
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3063 itemParams
->issuerAndSNToMatch
= (itemParams
->issuer
!= NULL
&& itemParams
->serialNumber
!= NULL
);
3065 // other input attributes, used for SecItemAdd but not for finding items
3066 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrAccess
, (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3067 if (itemParams
->access
== NULL
) {
3068 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3069 require_noerr(status
= _ValidateDictionaryEntry(dict
, CFSTR("kSecAttrAccess"), (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3072 // determine how to return the result
3073 itemParams
->numResultTypes
= 0;
3074 itemParams
->returningRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3075 if (itemParams
->returningRef
) ++itemParams
->numResultTypes
;
3076 itemParams
->returningPersistentRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnPersistentRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3077 if (itemParams
->returningPersistentRef
) ++itemParams
->numResultTypes
;
3078 itemParams
->returningAttributes
= CFDictionaryGetValueIfPresent(dict
, kSecReturnAttributes
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3079 if (itemParams
->returningAttributes
) ++itemParams
->numResultTypes
;
3080 itemParams
->returningData
= CFDictionaryGetValueIfPresent(dict
, kSecReturnData
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3081 if (itemParams
->returningData
) ++itemParams
->numResultTypes
;
3083 // default is kSecReturnRef if no result types were specified
3084 if (!itemParams
->numResultTypes
) {
3085 itemParams
->returningRef
= TRUE
;
3086 itemParams
->numResultTypes
= 1;
3089 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3090 itemParams
->maxMatches
= 1;
3091 itemParams
->returnAllMatches
= FALSE
;
3092 if (itemParams
->matchLimit
) {
3093 if (CFStringGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3094 itemParams
->returnAllMatches
= CFEqual(kSecMatchLimitAll
, itemParams
->matchLimit
);
3096 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3097 CFNumberGetValue((CFNumberRef
)itemParams
->matchLimit
, kCFNumberIntType
, &itemParams
->maxMatches
);
3098 require_action(!(itemParams
->maxMatches
< 0), error_exit
, status
= errSecMatchLimitUnsupported
);
3101 if (itemParams
->returnAllMatches
) {
3102 itemParams
->maxMatches
= INT32_MAX
;
3103 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3104 if ((itemParams
->itemClass
==kSecInternetPasswordItemClass
|| itemParams
->itemClass
==kSecGenericPasswordItemClass
) && itemParams
->returningData
)
3105 status
= errSecReturnDataUnsupported
;
3106 require_noerr(status
, error_exit
);
3109 // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference
3110 if (itemParams
->useItems
) {
3111 if (itemParams
->itemClass
== 0) {
3112 itemParams
->itemClass
= _ItemClassFromItemList(itemParams
->useItems
);
3114 status
= errSecSuccess
;
3115 goto error_exit
; // all done here
3118 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3119 require_noerr(status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
), error_exit
);
3121 // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
3122 if(itemParams
->policy
) {
3123 CFDictionaryRef policyDict
= SecPolicyCopyProperties(itemParams
->policy
);
3124 CFStringRef oidStr
= (CFStringRef
) CFDictionaryGetValue(policyDict
, kSecPolicyOid
);
3125 if(oidStr
&& CFStringCompare(kSecPolicyAppleSMIME
,oidStr
,0) == 0) {
3126 require_noerr(status
= _ValidateDictionaryEntry(policyDict
, kSecPolicyName
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
3128 CFRelease(policyDict
);
3131 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3132 if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->emailAddrToMatch
) {
3133 // searching for certificates by email address
3134 char *nameBuf
= (char*)malloc(MAXPATHLEN
);
3136 status
= errSecAllocate
;
3138 else if (CFStringGetCString((CFStringRef
)itemParams
->emailAddrToMatch
, nameBuf
, (CFIndex
)MAXPATHLEN
-1, kCFStringEncodingUTF8
)) {
3139 status
= SecKeychainSearchCreateForCertificateByEmail(itemParams
->searchList
, (const char *)nameBuf
, (SecKeychainSearchRef
*)&itemParams
->search
);
3142 status
= errSecItemInvalidValue
;
3144 if (nameBuf
) free(nameBuf
);
3146 else if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->issuerAndSNToMatch
) {
3147 // searching for certificates by issuer and serial number
3148 status
= SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams
->searchList
,
3149 (CFDataRef
)itemParams
->issuer
,
3150 (CFDataRef
)itemParams
->serialNumber
,
3151 (SecKeychainSearchRef
*)&itemParams
->search
);
3153 else if (itemParams
->returnIdentity
&& itemParams
->policy
) {
3154 // searching for identities by policy
3155 status
= SecIdentitySearchCreateWithPolicy(itemParams
->policy
,
3156 (CFStringRef
)itemParams
->service
,
3157 itemParams
->keyUsage
,
3158 itemParams
->searchList
,
3159 itemParams
->trustedOnly
,
3160 (SecIdentitySearchRef
*)&itemParams
->search
);
3162 else if (itemParams
->returnIdentity
) {
3163 // searching for identities
3164 status
= SecIdentitySearchCreate(itemParams
->searchList
,
3165 itemParams
->keyUsage
,
3166 (SecIdentitySearchRef
*)&itemParams
->search
);
3169 // normal keychain item search
3170 status
= SecKeychainSearchCreateFromAttributes(itemParams
->searchList
,
3171 itemParams
->itemClass
,
3172 (itemParams
->attrList
->count
== 0) ? NULL
: itemParams
->attrList
,
3173 (SecKeychainSearchRef
*)&itemParams
->search
);
3178 _FreeSecItemParams(itemParams
);
3191 SecKeychainRef keychainRef
,
3192 SecAccessRef accessRef
,
3193 SecKeychainAttributeList
*attrList
,
3194 SecKeychainItemRef
*outItemRef
)
3198 // We must specify the access, since a free-floating key won't have one yet by default
3199 SecPointer
<Access
> access
;
3201 access
= Access::required(accessRef
);
3204 CFStringRef descriptor
= NULL
;
3206 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
3207 SecKeychainAttribute attr
= attrList
->attr
[index
];
3208 if (attr
.tag
== kSecKeyPrintName
) {
3209 descriptor
= CFStringCreateWithBytes(NULL
, (const UInt8
*)attr
.data
, attr
.length
, kCFStringEncodingUTF8
, FALSE
);
3214 if (descriptor
== NULL
) {
3215 descriptor
= (CFStringRef
) CFRetain(CFSTR("<unknown>"));
3217 access
= new Access(cfString(descriptor
));
3218 CFRelease(descriptor
);
3221 KeyItem
*key
= KeyItem::required(keyRef
);
3222 Item item
= key
->importTo(Keychain::optional(keychainRef
), access
, attrList
);
3224 *outItemRef
= item
->handle();
3231 _CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
)
3233 /* Check for ignorable status codes in leaf certificate's evidence */
3234 Boolean result
= true;
3236 for (i
=0; i
< evidence
->NumStatusCodes
; i
++) {
3237 CSSM_RETURN scode
= evidence
->StatusCodes
[i
];
3238 if (scode
== CSSMERR_APPLETP_INVALID_CA
) {
3239 // the TP has rejected this CA cert because it's in the leaf position
3242 else if (ignorableRevocationStatusCode(scode
)) {
3255 _FilterWithPolicy(SecPolicyRef policy
, CFDateRef date
, SecCertificateRef cert
)
3257 CFDictionaryRef props
= NULL
;
3258 CFArrayRef keychains
= NULL
;
3259 CFArrayRef anchors
= NULL
;
3260 CFArrayRef certs
= NULL
;
3261 CFArrayRef chain
= NULL
;
3262 SecTrustRef trust
= NULL
;
3264 SecTrustResultType trustResult
;
3266 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
= NULL
;
3268 Boolean needChain
= false;
3270 if (!policy
|| !cert
) return errSecParam
;
3272 certs
= CFArrayCreate(NULL
, (const void **)&cert
, (CFIndex
)1, &kCFTypeArrayCallBacks
);
3273 status
= SecTrustCreateWithCertificates(certs
, policy
, &trust
);
3274 if(status
) goto cleanup
;
3276 /* Set evaluation date, if specified (otherwise current date is implied) */
3277 if (date
&& (CFGetTypeID(date
) == CFDateGetTypeID())) {
3278 status
= SecTrustSetVerifyDate(trust
, date
);
3279 if(status
) goto cleanup
;
3282 /* Check whether this is the X509 Basic policy, which means chain building */
3283 props
= SecPolicyCopyProperties(policy
);
3285 CFTypeRef oid
= (CFTypeRef
) CFDictionaryGetValue(props
, kSecPolicyOid
);
3286 if (oid
&& (CFEqual(oid
, kSecPolicyAppleX509Basic
) ||
3287 CFEqual(oid
, kSecPolicyAppleRevocation
))) {
3294 /* To make the evaluation as lightweight as possible, specify an empty array
3295 * of keychains which will be searched for certificates.
3297 keychains
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
3298 status
= SecTrustSetKeychains(trust
, keychains
);
3299 if(status
) goto cleanup
;
3301 /* To make the evaluation as lightweight as possible, specify an empty array
3302 * of trusted anchors.
3304 anchors
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
3305 status
= SecTrustSetAnchorCertificates(trust
, anchors
);
3306 if(status
) goto cleanup
;
3308 status
= SecTrustEvaluateLeafOnly(trust
, &trustResult
);
3310 status
= SecTrustEvaluate(trust
, &trustResult
);
3315 /* All parameters are locked and loaded, ready to evaluate! */
3316 status
= SecTrustEvaluate(trust
, &trustResult
);
3317 if(status
) goto cleanup
;
3319 /* If we didn't provide trust anchors or a way to look for them,
3320 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
3321 * However, we can tell whether the policy evaluation succeeded by
3322 * looking at the per-cert status codes in the returned evidence.
3324 status
= SecTrustGetResult(trust
, &trustResult
, &chain
, &evidence
);
3325 if(status
) goto cleanup
;
3328 if (!(trustResult
== kSecTrustResultProceed
||
3329 trustResult
== kSecTrustResultUnspecified
||
3330 trustResult
== kSecTrustResultRecoverableTrustFailure
)) {
3331 /* The evaluation failed in a non-recoverable way */
3332 status
= errSecCertificateCannotOperate
;
3336 /* If there are no per-cert policy status codes,
3337 * and the cert has not expired, consider it valid for the policy.
3341 (void)SecTrustGetCssmResultCode(trust
, &status
);
3343 if((evidence
!= NULL
) && _CanIgnoreLeafStatusCodes(evidence
) &&
3344 ((evidence
[0].StatusBits
& CSSM_CERT_STATUS_EXPIRED
) == 0) &&
3345 ((evidence
[0].StatusBits
& CSSM_CERT_STATUS_NOT_VALID_YET
) == 0)) {
3346 status
= errSecSuccess
;
3350 status
= errSecCertificateCannotOperate
;
3354 if(props
) CFRelease(props
);
3355 if(chain
) CFRelease(chain
);
3356 if(anchors
) CFRelease(anchors
);
3357 if(keychains
) CFRelease(keychains
);
3358 if(certs
) CFRelease(certs
);
3359 if(trust
) CFRelease(trust
);
3365 _FilterWithDate(CFTypeRef validOnDate
, SecCertificateRef cert
)
3367 if (!validOnDate
|| !cert
) return errSecParam
;
3369 CFAbsoluteTime at
, nb
, na
;
3370 if (CFGetTypeID(validOnDate
) == CFDateGetTypeID())
3371 at
= CFDateGetAbsoluteTime((CFDateRef
)validOnDate
);
3373 at
= CFAbsoluteTimeGetCurrent();
3375 OSStatus status
= errSecSuccess
;
3376 nb
= SecCertificateNotValidBefore(cert
);
3377 na
= SecCertificateNotValidAfter(cert
);
3379 if (nb
== 0 || na
== 0 || nb
== na
)
3380 status
= errSecCertificateCannotOperate
;
3382 status
= errSecCertificateNotValidYet
;
3384 status
= errSecCertificateExpired
;
3390 _FilterWithTrust(Boolean trustedOnly
, SecCertificateRef cert
)
3392 if (!cert
) return errSecParam
;
3393 if (!trustedOnly
) return errSecSuccess
;
3395 CFArrayRef certArray
= CFArrayCreate(NULL
, (const void**)&cert
, 1, &kCFTypeArrayCallBacks
);
3396 SecPolicyRef policy
= SecPolicyCreateWithOID(kSecPolicyAppleX509Basic
);
3397 OSStatus status
= (policy
== NULL
) ? errSecPolicyNotFound
: errSecSuccess
;
3400 SecTrustRef trust
= NULL
;
3401 status
= SecTrustCreateWithCertificates(certArray
, policy
, &trust
);
3403 SecTrustResultType trustResult
;
3404 status
= SecTrustEvaluate(trust
, &trustResult
);
3406 if (!(trustResult
== kSecTrustResultProceed
|| trustResult
== kSecTrustResultUnspecified
)) {
3407 status
= (trustResult
== kSecTrustResultDeny
) ? errSecTrustSettingDeny
: errSecNotTrusted
;
3415 CFRelease(certArray
);
3421 static SecKeychainItemRef
3422 CopyResolvedKeychainItem(CFTypeRef item
)
3424 SecKeychainItemRef kcItem
= NULL
;
3425 OSStatus status
= errSecSuccess
;
3427 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
3428 // persistent reference, resolve first
3429 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &kcItem
);
3433 kcItem
= (SecKeychainItemRef
) CFRetain(item
);
3436 // ask for the item's class:
3437 // will return an error if the item has been deleted
3438 SecItemClass itemClass
;
3439 SecCertificateRef certRef
= NULL
;
3440 CFTypeID itemTypeID
= CFGetTypeID(kcItem
);
3441 if (itemTypeID
== SecIdentityGetTypeID()) {
3442 status
= SecIdentityCopyCertificate((SecIdentityRef
)kcItem
, &certRef
);
3444 else if (itemTypeID
== SecCertificateGetTypeID()) {
3445 certRef
= (SecCertificateRef
) CFRetain(kcItem
);
3448 // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef
3449 itemClass
= kSecCertificateItemClass
;
3452 status
= SecKeychainItemCopyAttributesAndData(kcItem
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3467 UpdateKeychainSearchAndCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3469 // This function refreshes the search parameters in the specific case where
3470 // the caller is searching for kSecClassKey items but did not provide the
3471 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3472 // we must perform separate searches to obtain all results.
3474 OSStatus status
= errSecItemNotFound
;
3475 if (!params
|| !params
->assumedKeyClass
|| !params
->query
|| !item
)
3478 // Free the previous search reference and attribute list.
3480 CFRelease(params
->search
);
3481 params
->search
= NULL
;
3482 _FreeAttrList(params
->attrList
);
3483 params
->attrList
= NULL
;
3485 // Make a copy of the query dictionary so we can set the key class parameter.
3486 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(NULL
, 0, params
->query
);
3487 CFRelease(params
->query
);
3488 params
->query
= dict
;
3489 CFDictionarySetValue(dict
, kSecAttrKeyClass
, params
->assumedKeyClass
);
3491 // Determine the current item class for this search, and the next assumed key class.
3492 if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassSymmetric
)) {
3493 params
->itemClass
= kSecSymmetricKeyItemClass
;
3494 params
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3495 } else if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassPublic
)) {
3496 params
->itemClass
= kSecPublicKeyItemClass
;
3497 params
->assumedKeyClass
= kSecAttrKeyClassPrivate
;
3499 params
->itemClass
= kSecPrivateKeyItemClass
;
3500 params
->assumedKeyClass
= NULL
;
3503 // Rebuild the attribute list for the new key class.
3504 if (_CreateSecKeychainAttributeListFromDictionary(dict
, params
->itemClass
, ¶ms
->attrList
) == errSecSuccess
) {
3505 // Create a new search reference for the new attribute list.
3506 if (SecKeychainSearchCreateFromAttributes(params
->searchList
,
3508 (params
->attrList
->count
== 0) ? NULL
: params
->attrList
,
3509 (SecKeychainSearchRef
*)¶ms
->search
) == errSecSuccess
) {
3510 // Return the first matching item from the new search.
3511 // We won't come back here again until there are no more matching items for this search.
3512 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)params
->search
, (SecKeychainItemRef
*)item
);
3520 SecItemSearchCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3522 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3523 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3524 // depending on the type of search reference.
3527 CFTypeRef search
= (params
) ? params
->search
: NULL
;
3528 CFTypeID typeID
= (search
) ? CFGetTypeID(search
) : 0;
3529 if (typeID
== SecIdentitySearchGetTypeID()) {
3530 status
= SecIdentitySearchCopyNext((SecIdentitySearchRef
)search
, (SecIdentityRef
*)item
);
3532 else if (typeID
== SecKeychainSearchGetTypeID()) {
3533 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)search
, (SecKeychainItemRef
*)item
);
3534 // Check if we need to refresh the search for the next key class
3535 while (status
== errSecItemNotFound
&& params
->assumedKeyClass
!= NULL
)
3536 status
= UpdateKeychainSearchAndCopyNext(params
, item
);
3538 else if (typeID
== 0 && (params
->useItems
|| params
->itemList
)) {
3539 // No search available, but there is an item list available.
3540 // Return the next candidate item from the caller's item list
3541 CFArrayRef itemList
= (params
->useItems
) ? params
->useItems
: params
->itemList
;
3542 CFIndex count
= CFArrayGetCount(itemList
);
3543 *item
= (CFTypeRef
) NULL
;
3544 if (params
->itemListIndex
< count
) {
3545 *item
= (CFTypeRef
)CFArrayGetValueAtIndex(itemList
, params
->itemListIndex
++);
3547 // Potentially resolve persistent item references here, and
3548 // verify the item reference we're about to hand back is still
3549 // valid (it could have been deleted from the keychain while
3550 // our query was holding onto the itemList).
3551 *item
= CopyResolvedKeychainItem(*item
);
3552 if (*item
&& (CFGetTypeID(*item
) == SecIdentityGetTypeID())) {
3553 // Persistent reference resolved to an identity, so return that type.
3554 params
->returnIdentity
= true;
3558 status
= (*item
) ? errSecSuccess
: errSecItemNotFound
;
3561 status
= errSecItemNotFound
;
3567 FilterCandidateItem(CFTypeRef
*item
, SecItemParams
*itemParams
, SecIdentityRef
*identity
)
3569 if (!item
|| *item
== NULL
|| !itemParams
)
3570 return errSecItemNotFound
;
3573 CFStringRef commonName
= NULL
;
3574 SecIdentityRef foundIdentity
= NULL
;
3575 if (CFGetTypeID(*item
) == SecIdentityGetTypeID()) {
3576 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3577 // replace the found "item" with its associated certificate (which is the
3578 // item we actually want for purposes of getting attributes, data, or a
3579 // persistent data reference), and return the identity separately.
3580 SecCertificateRef certificate
;
3581 status
= SecIdentityCopyCertificate((SecIdentityRef
) *item
, &certificate
);
3582 if (itemParams
->returnIdentity
) {
3583 foundIdentity
= (SecIdentityRef
) *item
;
3585 *identity
= foundIdentity
;
3591 *item
= (CFTypeRef
)certificate
;
3594 CFDictionaryRef query
= itemParams
->query
;
3596 if (itemParams
->itemClass
== kSecCertificateItemClass
) {
3597 // perform string comparisons first
3598 CFStringCompareFlags flags
= _StringCompareFlagsFromQuery(query
);
3599 CFStringRef nameContains
, nameStarts
, nameEnds
, nameExact
;
3600 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectContains
, (const void **)&nameContains
))
3601 nameContains
= NULL
;
3602 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&nameStarts
))
3604 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&nameEnds
))
3606 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectWholeString
, (const void **)&nameExact
))
3608 if (nameContains
|| nameStarts
|| nameEnds
|| nameExact
) {
3609 status
= SecCertificateCopyCommonName((SecCertificateRef
)*item
, &commonName
);
3610 if (status
|| !commonName
) goto filterOut
;
3613 CFRange range
= CFStringFind(commonName
, nameContains
, flags
);
3614 if (range
.length
< 1)
3616 // certificate item contains string; proceed to next check
3619 CFRange range
= CFStringFind(commonName
, nameStarts
, flags
);
3620 if (range
.length
< 1 || range
.location
> 1)
3622 // certificate item starts with string; proceed to next check
3625 CFRange range
= CFStringFind(commonName
, nameEnds
, flags
);
3626 if (range
.length
< 1 || range
.location
!= (CFStringGetLength(commonName
) - CFStringGetLength(nameEnds
)))
3628 // certificate item ends with string; proceed to next check
3631 CFRange range
= CFStringFind(commonName
, nameExact
, flags
);
3632 if (range
.length
< 1 || (CFStringGetLength(commonName
) != CFStringGetLength(nameExact
)))
3634 // certificate item exactly matches string; proceed to next check
3636 if (itemParams
->returnIdentity
) {
3637 // if we already found and returned the identity, we can skip this
3638 if (!foundIdentity
) {
3639 status
= SecIdentityCreateWithCertificate(itemParams
->searchList
, (SecCertificateRef
) *item
, identity
);
3640 if (status
) goto filterOut
;
3642 // certificate item is part of an identity; proceed to next check
3644 if (itemParams
->policy
) {
3645 status
= _FilterWithPolicy(itemParams
->policy
, (CFDateRef
)itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3646 if (status
) goto filterOut
;
3647 // certificate item is valid for specified policy (and optionally specified date)
3649 if (itemParams
->validOnDate
) {
3650 status
= _FilterWithDate(itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3651 if (status
) goto filterOut
;
3652 // certificate item is valid for specified date
3654 if (itemParams
->trustedOnly
) {
3655 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3656 // their trust has already been validated and we can skip this part.
3657 if (!(foundIdentity
&& itemParams
->returnIdentity
&& itemParams
->policy
)) {
3658 status
= _FilterWithTrust(itemParams
->trustedOnly
, (SecCertificateRef
) *item
);
3659 if (status
) goto filterOut
;
3661 // certificate item is trusted on this system
3664 if (itemParams
->itemList
) {
3665 Boolean foundMatch
= FALSE
;
3666 CFIndex idx
, count
= CFArrayGetCount(itemParams
->itemList
);
3667 for (idx
=0; idx
<count
; idx
++) {
3668 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->itemList
, idx
);
3669 SecKeychainItemRef realItem
= NULL
;
3670 SecCertificateRef aCert
= NULL
;
3671 if (anItem
== NULL
) {
3674 if (CFDataGetTypeID() == CFGetTypeID(anItem
) &&
3675 errSecSuccess
== SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
)) {
3678 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
) &&
3679 errSecSuccess
== SecIdentityCopyCertificate((SecIdentityRef
)anItem
, &aCert
)) {
3682 if (CFEqual(anItem
, (CFTypeRef
) *item
)) {
3689 CFRelease(realItem
);
3695 if (!foundMatch
) goto filterOut
;
3696 // item was found on provided list
3699 if (foundIdentity
&& !identity
) {
3700 CFRelease(foundIdentity
);
3703 CFRelease(commonName
);
3706 // if we get here, consider the item a match
3707 return errSecSuccess
;
3711 CFRelease(commonName
);
3715 if (foundIdentity
) {
3716 CFRelease(foundIdentity
);
3721 return errSecItemNotFound
;
3725 AddItemResults(SecKeychainItemRef item
,
3726 SecIdentityRef identity
,
3727 SecItemParams
*itemParams
,
3728 CFAllocatorRef allocator
,
3729 CFMutableArrayRef
*items
,
3732 // Given a found item (which may also be an identity), this function adds
3733 // the requested result types (specified in itemParams) to the appropriate
3734 // container as follows:
3736 // 1. If there is only one result type (numResultTypes == 1) and only one
3737 // match requested (maxMatches == 1), set *result directly.
3739 // 2. If there are multiple result types (numResultTypes > 1), and only one
3740 // match requested (maxMatches == 1), add each result type to itemDict
3741 // and set itemDict as the value of *result.
3743 // 3. If there is only one result type (numResultTypes == 1) and multiple
3744 // possible matches (maxMatches > 1), add the result type to *items
3745 // and set *items as the value of *result.
3747 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3748 // possible matches (maxMatches > 1), add each result type to itemDict,
3749 // add itemDict to *items, and set *items as the value of *result.
3751 // Note that we allocate *items if needed.
3753 if (!item
|| !itemParams
|| !result
)
3756 if (itemParams
->maxMatches
> 1) {
3757 // if we can return more than one item, we must have an array
3760 else if (*items
== NULL
)
3761 *items
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3764 OSStatus tmpStatus
, status
= errSecSuccess
;
3765 CFMutableArrayRef itemArray
= (items
) ? *items
: NULL
;
3766 CFMutableDictionaryRef itemDict
= NULL
;
3767 if (itemParams
->numResultTypes
> 1) {
3768 // if we're returning more than one result type, each item we return must be a dictionary
3769 itemDict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3772 if (itemParams
->returningRef
) {
3773 const void* itemRef
= (identity
) ? (const void*)identity
: (const void*)item
;
3775 CFDictionaryAddValue(itemDict
, kSecValueRef
, itemRef
);
3777 else if (itemArray
) {
3778 CFArrayAppendValue(itemArray
, itemRef
);
3781 *result
= CFRetain((CFTypeRef
)itemRef
);
3785 if (itemParams
->returningPersistentRef
) {
3786 CFDataRef persistentRef
;
3787 SecKeychainItemRef tmpItem
= item
;
3788 if (itemParams
->identityRef
) {
3789 tmpItem
= (SecKeychainItemRef
)itemParams
->identityRef
;
3791 tmpStatus
= SecKeychainItemCreatePersistentReference(tmpItem
, &persistentRef
);
3792 if (tmpStatus
== errSecSuccess
) {
3794 CFDictionaryAddValue(itemDict
, kSecValuePersistentRef
, persistentRef
);
3796 else if (itemArray
) {
3797 CFArrayAppendValue(itemArray
, persistentRef
);
3800 *result
= CFRetain(persistentRef
);
3802 CFRelease(persistentRef
);
3804 else if (status
== errSecSuccess
) {
3809 if (itemParams
->returningData
) {
3810 // Use SecCertificateCopyData if we have a SecCertificateRef item.
3811 // Note that a SecCertificateRef may not actually be a SecKeychainItem,
3812 // in which case SecKeychainItemCopyContent will not obtain its data.
3814 if (CFGetTypeID(item
) == SecCertificateGetTypeID()) {
3815 CFDataRef dataRef
= SecCertificateCopyData((SecCertificateRef
)item
);
3818 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3820 else if (itemArray
) {
3821 CFArrayAppendValue(itemArray
, dataRef
);
3824 *result
= CFRetain(dataRef
);
3827 status
= errSecSuccess
;
3830 status
= errSecAllocate
;
3836 tmpStatus
= SecKeychainItemCopyContent(item
, NULL
, NULL
, &length
, &data
);
3837 if (tmpStatus
== errSecSuccess
) {
3838 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)data
, length
);
3840 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3842 else if (itemArray
) {
3843 CFArrayAppendValue(itemArray
, dataRef
);
3846 *result
= CFRetain(dataRef
);
3849 (void) SecKeychainItemFreeContent(NULL
, data
);
3851 else if (status
== errSecSuccess
) {
3857 if (itemParams
->returningAttributes
) {
3858 CFDictionaryRef attrsDict
= NULL
;
3859 SecItemClass itemClass
;
3860 // since we have an item, allow its actual class to override the query-specified item class
3861 tmpStatus
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3863 itemClass
= itemParams
->itemClass
;
3865 tmpStatus
= _CreateAttributesDictionaryFromItem(allocator
, itemClass
, item
, &attrsDict
);
3868 // add all keys and values from attrsDict to the item dictionary
3869 CFDictionaryApplyFunction(attrsDict
, _AddDictValueToOtherDict
, &itemDict
);
3871 else if (itemArray
) {
3872 CFArrayAppendValue(itemArray
, attrsDict
);
3875 *result
= CFRetain(attrsDict
);
3877 CFRelease(attrsDict
);
3879 if (tmpStatus
&& (status
== errSecSuccess
)) {
3886 CFArrayAppendValue(itemArray
, itemDict
);
3887 CFRelease(itemDict
);
3888 *result
= itemArray
;
3894 else if (itemArray
) {
3895 *result
= itemArray
;
3901 CFDataRef
_SecItemGetPersistentReference(CFTypeRef raw_item
)
3904 Item item
= ItemImpl::required((SecKeychainItemRef
)raw_item
);
3905 return item
->getPersistentRef();
3911 /******************************************************************************/
3912 #pragma mark SecItem API functions
3913 /******************************************************************************/
3916 // Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3918 static SInt32
readNumber(CFTypeRef obj
) {
3919 CFTypeID tid
= CFGetTypeID(obj
);
3921 if (tid
== CFNumberGetTypeID()) {
3922 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt32Type
, &v
);
3924 } else if (tid
== CFBooleanGetTypeID()) {
3925 v
= CFBooleanGetValue((CFBooleanRef
)obj
);
3927 } else if (tid
== CFStringGetTypeID()) {
3928 v
= CFStringGetIntValue((CFStringRef
)obj
);
3929 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v
);
3930 /* If a string converted to an int isn't equal to the int printed as
3931 a string, return a CFStringRef instead. */
3932 if (!CFEqual(t
, obj
)) {
3943 // Function to check whether the kSecAttrSynchronizable flag is set in the query.
3945 static Boolean
SecItemSynchronizable(CFDictionaryRef query
)
3947 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
);
3948 Boolean result
= (value
&& readNumber(value
));
3954 // Function to check whether a synchronizable persistent reference was provided.
3956 static Boolean
SecItemIsIOSPersistentReference(CFTypeRef value
)
3959 /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
3960 const CFIndex kSynchronizablePersistentRefLength
= sizeof(int64_t) + 4;
3961 return (CFGetTypeID(value
) == CFDataGetTypeID() &&
3962 CFDataGetLength((CFDataRef
)value
) == kSynchronizablePersistentRefLength
);
3967 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
3970 // Function to find out which keychains are targetted by the query.
3972 static OSStatus
SecItemCategorizeQuery(CFDictionaryRef query
, bool &can_target_ios
, bool &can_target_osx
)
3974 // By default, target both keychain.
3975 can_target_osx
= can_target_ios
= true;
3977 // Check no-legacy flag.
3978 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrNoLegacy
);
3979 if (value
!= NULL
) {
3980 can_target_ios
= readNumber(value
) != 0;
3981 can_target_osx
= !can_target_ios
;
3982 return errSecSuccess
;
3985 // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value.
3986 value
= CFDictionaryGetValue(query
, kSecValueRef
);
3987 if (value
!= NULL
) {
3988 CFTypeID typeID
= CFGetTypeID(value
);
3989 if (typeID
== SecKeyGetTypeID()) {
3990 can_target_osx
= SecKeyIsCDSAKey((SecKeyRef
)value
);
3991 can_target_ios
= !can_target_osx
;
3992 } else if (typeID
== SecCertificateGetTypeID()) {
3993 // All types of certificates can target OSX keychains, but OSX certificates won't work on iOS
3994 can_target_ios
&= !SecCertificateIsItemImplInstance((SecCertificateRef
)value
);
3995 } else if (typeID
== SecKeychainItemGetTypeID()) {
3996 // SecKeychainItemRef can target iOS keychain only when it has attached iOS-style persistent reference.
3997 if (_SecItemGetPersistentReference(value
) == NULL
) {
3998 can_target_ios
= false;
4003 // Check presence of kSecAttrTokenID and kSecAttrAccessControl; they are not defined for CDSA keychain.
4004 if (CFDictionaryContainsKey(query
, kSecAttrTokenID
) || CFDictionaryContainsKey(query
, kSecAttrAccessControl
)) {
4005 can_target_osx
= false;
4008 // Check for special token access groups. If present, redirect query to iOS keychain.
4009 value
= CFDictionaryGetValue(query
, kSecAttrAccessGroup
);
4010 if (value
!= NULL
&& CFEqual(value
, kSecAttrAccessGroupToken
)) {
4011 can_target_osx
= false;
4014 // Synchronizable items should go to iOS keychain only.
4015 if (SecItemSynchronizable(query
)) {
4016 can_target_osx
= false;
4019 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
4020 if (value
!= NULL
) {
4021 if (SecItemIsIOSPersistentReference(value
)) {
4022 can_target_osx
= false;
4024 // Non-iOS-style persistent references should not be fed to iOS keychain queries.
4025 can_target_ios
= false;
4029 // Presence of following atributes means that query is OSX-only.
4030 static const CFStringRef
*osx_only_items
[] = {
4032 &kSecMatchSearchList
,
4033 &kSecMatchSubjectStartsWith
,
4034 &kSecMatchSubjectEndsWith
,
4035 &kSecMatchSubjectWholeString
,
4036 &kSecMatchDiacriticInsensitive
,
4037 &kSecMatchWidthInsensitive
,
4046 for (CFIndex i
= 0; i
< array_size(osx_only_items
); i
++) {
4047 can_target_ios
= can_target_ios
&& !CFDictionaryContainsKey(query
, *osx_only_items
[i
]);
4050 return (can_target_ios
|| can_target_osx
) ? errSecSuccess
: errSecParam
;
4054 // Function to check whether the kSecAttrSynchronizable attribute is being updated.
4056 static Boolean
SecItemHasSynchronizableUpdate(Boolean synchronizable
, CFDictionaryRef changes
)
4058 CFTypeRef newValue
= CFDictionaryGetValue(changes
, kSecAttrSynchronizable
);
4062 Boolean new_sync
= readNumber(newValue
);
4063 Boolean old_sync
= synchronizable
;
4065 return (old_sync
!= new_sync
);
4069 // Function to apply changes to a mutable dictionary.
4070 // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
4072 static void SecItemApplyChanges(const void *key
, const void *value
, void *context
)
4074 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
) context
;
4077 CFDictionarySetValue(dict
, key
, value
);
4081 // Function to change matching items from non-syncable to syncable
4082 // (if toSyncable is true), otherwise from syncable to non-syncable.
4083 // This currently moves items between keychain containers.
4085 static OSStatus
SecItemChangeSynchronizability(CFDictionaryRef query
, CFDictionaryRef changes
, Boolean toSyncable
)
4087 // Note: the input query dictionary is a mutable copy of the query originally
4088 // provided by the caller as the first parameter to SecItemUpdate. It may not
4089 // specify returning attributes or data, but we will need both to make a copy.
4091 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnRef
);
4092 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnPersistentRef
);
4093 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnData
);
4094 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnAttributes
, kCFBooleanTrue
);
4095 if (NULL
== CFDictionaryGetValue(changes
, kSecValueData
))
4096 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnData
, kCFBooleanTrue
);
4101 status
= SecItemCopyMatching_osx(query
, &result
);
4103 status
= SecItemCopyMatching_ios(query
, &result
);
4108 return errSecItemNotFound
;
4110 CFMutableArrayRef items
;
4111 if (CFGetTypeID(result
) != CFArrayGetTypeID()) {
4112 items
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4113 CFArrayAppendValue(items
, result
);
4117 items
= (CFMutableArrayRef
)result
;
4120 CFIndex idx
, count
= (items
) ? CFArrayGetCount(items
) : 0;
4121 int priority
= LOG_DEBUG
;
4123 for (idx
= 0; idx
< count
; idx
++) {
4124 CFDictionaryRef dict
= (CFDictionaryRef
) CFArrayGetValueAtIndex(items
, idx
);
4125 CFMutableDictionaryRef item
= (CFMutableDictionaryRef
)
4126 SecItemCopyTranslatedAttributes(dict
,
4127 CFDictionaryGetValue(query
, kSecClass
),
4128 (toSyncable
) ? true : false /*iOSOut*/,
4129 true /*pruneMatch*/,
4131 true /*pruneReturn*/,
4132 false /*pruneData*/,
4133 (toSyncable
) ? true : false /*pruneAccess*/);
4134 // hold onto the query before applying changes, in case the item already exists.
4135 // note that we cannot include the creation or modification dates from our
4136 // found item in this query, as they may not match the item in the other keychain.
4137 CFMutableDictionaryRef itemQuery
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
4138 CFDictionaryRemoveValue(itemQuery
, kSecAttrCreationDate
);
4139 CFDictionaryRemoveValue(itemQuery
, kSecAttrModificationDate
);
4140 // apply changes to the item dictionary that we will pass to SecItemAdd
4141 CFDictionaryApplyFunction(changes
, SecItemApplyChanges
, item
);
4143 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4144 status
= SecItemAdd_ios(item
, NULL
);
4145 secitemlog(priority
, "ChangeSync: SecItemAdd_ios=%d", status
);
4146 if (errSecDuplicateItem
== status
) {
4147 // find and apply changes to the existing syncable item.
4148 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4149 status
= SecItemUpdate_ios(itemQuery
, changes
);
4150 secitemlog(priority
, "ChangeSync: SecItemUpdate_ios=%d", status
);
4152 if (errSecSuccess
== status
) {
4153 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4154 status
= SecItemDelete_osx(itemQuery
);
4155 secitemlog(priority
, "ChangeSync: SecItemDelete_osx=%d", status
);
4159 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4160 status
= SecItemAdd_osx(item
, NULL
);
4161 secitemlog(priority
, "ChangeSync: SecItemAdd_osx=%d", status
);
4162 if (errSecDuplicateItem
== status
) {
4163 // find and apply changes to the existing non-syncable item.
4164 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4165 status
= SecItemUpdate_osx(itemQuery
, changes
);
4166 secitemlog(priority
, "ChangeSync: SecItemUpdate_osx=%d", status
);
4168 if (errSecSuccess
== status
) {
4169 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4170 status
= SecItemDelete_ios(itemQuery
);
4171 secitemlog(priority
, "ChangeSync: SecItemDelete_ios=%d", status
);
4174 CFReleaseSafe(item
);
4175 CFReleaseSafe(itemQuery
);
4179 CFReleaseSafe(items
);
4188 SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
) {
4189 CFTypeRef ref
= NULL
;
4190 CFStringRef item_class_string
= (CFStringRef
)CFDictionaryGetValue(refAttributes
, kSecClass
);
4191 SecItemClass item_class
= 0;
4193 if (CFEqual(item_class_string
, kSecClassGenericPassword
)) {
4194 item_class
= kSecGenericPasswordItemClass
;
4195 } else if (CFEqual(item_class_string
, kSecClassInternetPassword
)) {
4196 item_class
= kSecInternetPasswordItemClass
;
4199 if (item_class
!= 0) {
4200 // we carry v_Data around here so the *_ios calls can find it and locate
4201 // their own data. Putting things in the attribute list doesn't help as
4202 // the osx keychainitem and item calls bail when they don't see a keychain
4203 // object. If we need to make them work we either have to bridge them, or
4204 // find a way to craft a workable keychain object. #if'ed code left below
4205 // in case we need to go down that path.
4207 SecKeychainAttributeList attrs
= {};
4208 SecKeychainAttribute attr
= {};
4214 Item item
= Item(item_class
, &attrs
, 0, "");
4215 v
= CFDictionaryGetValue(refAttributes
, kSecValuePersistentRef
);
4217 item
->setPersistentRef((CFDataRef
)v
);
4219 ref
= item
->handle();
4226 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4227 * is a member of the specified application group, and is signed by Apple.
4230 SecItemValidateAppleApplicationGroupAccess(CFStringRef group
)
4232 SecTrustedApplicationRef app
= NULL
;
4233 SecRequirementRef requirement
= NULL
;
4234 SecCodeRef code
= NULL
;
4235 OSStatus status
= errSecParam
;
4238 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(group
), kCFStringEncodingUTF8
) + 1;
4239 char* buffer
= (char*) malloc(length
);
4241 if (CFStringGetCString(group
, buffer
, length
, kCFStringEncodingUTF8
)) {
4242 status
= SecTrustedApplicationCreateApplicationGroup(buffer
, NULL
, &app
);
4246 status
= errSecMemoryError
;
4250 status
= SecTrustedApplicationCopyRequirement(app
, &requirement
);
4253 status
= SecCodeCopySelf(kSecCSDefaultFlags
, &code
);
4256 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, requirement
);
4259 CFReleaseSafe(code
);
4260 CFReleaseSafe(requirement
);
4265 static Mutex gParentCertCacheLock
;
4266 static CFMutableDictionaryRef gParentCertCache
;
4267 static CFMutableArrayRef gParentCertCacheList
;
4268 #define PARENT_CACHE_SIZE 100
4270 void SecItemParentCachePurge() {
4271 StLock
<Mutex
> _(gParentCertCacheLock
);
4272 CFReleaseNull(gParentCertCache
);
4273 CFReleaseNull(gParentCertCacheList
);
4276 static CFArrayRef
parentCacheRead(SecCertificateRef certificate
) {
4277 CFArrayRef parents
= NULL
;
4279 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4280 if (!digest
) return NULL
;
4282 StLock
<Mutex
> _(gParentCertCacheLock
);
4283 if (gParentCertCache
&& gParentCertCacheList
) {
4284 if (0 <= (ix
= CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4285 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4287 // Cache hit. Get value and move entry to the top of the list.
4288 parents
= (CFArrayRef
)CFDictionaryGetValue(gParentCertCache
, digest
);
4289 CFArrayRemoveValueAtIndex(gParentCertCacheList
, ix
);
4290 CFArrayAppendValue(gParentCertCacheList
, digest
);
4293 CFRetainSafe(parents
);
4297 static void parentCacheWrite(SecCertificateRef certificate
, CFArrayRef parents
) {
4298 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4299 if (!digest
) return;
4301 StLock
<Mutex
> _(gParentCertCacheLock
);
4302 if (!gParentCertCache
|| !gParentCertCacheList
) {
4303 CFReleaseNull(gParentCertCache
);
4304 gParentCertCache
= makeCFMutableDictionary();
4305 CFReleaseNull(gParentCertCacheList
);
4306 gParentCertCacheList
= makeCFMutableArray(0);
4309 if (gParentCertCache
&& gParentCertCacheList
) {
4310 // check to make sure another thread didn't add this entry to the cache already
4311 if (0 > CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4312 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4314 CFDictionaryAddValue(gParentCertCache
, digest
, parents
);
4315 if (PARENT_CACHE_SIZE
<= CFArrayGetCount(gParentCertCacheList
)) {
4316 // Remove least recently used cache entry.
4317 CFArrayRemoveValueAtIndex(gParentCertCacheList
, 0);
4319 CFArrayAppendValue(gParentCertCacheList
, digest
);
4325 * SecItemCopyParentCertificates returns an array of zero of more possible
4326 * issuer certificates for the provided certificate. No cryptographic validation
4327 * of the signature is performed in this function; its purpose is only to
4328 * provide a list of candidate certificates.
4331 SecItemCopyParentCertificates(SecCertificateRef certificate
, void *context
)
4333 #pragma unused (context) /* for now; in future this can reference a container object */
4334 /* Check for parents in keychain cache */
4335 CFArrayRef parents
= parentCacheRead(certificate
);
4340 /* Cache miss. Query for parents. */
4341 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4342 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4344 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4345 CFRetainSafe(normalizedIssuer
);
4348 CFMutableArrayRef combinedSearchList
= NULL
;
4350 /* Define the array of keychains which will be searched for parents. */
4351 CFArrayRef searchList
= NULL
;
4352 status
= SecKeychainCopySearchList(&searchList
);
4354 combinedSearchList
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, searchList
);
4355 CFRelease(searchList
);
4357 combinedSearchList
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4359 SecKeychainRef rootStoreKeychain
= NULL
;
4360 status
= SecKeychainOpen(SYSTEM_ROOT_STORE_PATH
, &rootStoreKeychain
);
4361 if (rootStoreKeychain
) {
4362 if (combinedSearchList
) {
4363 CFArrayAppendValue(combinedSearchList
, rootStoreKeychain
);
4365 CFRelease(rootStoreKeychain
);
4368 /* Create and populate a fixed-size query dictionary. */
4369 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 5,
4370 &kCFTypeDictionaryKeyCallBacks
,
4371 &kCFTypeDictionaryValueCallBacks
);
4372 CFDictionaryAddValue(query
, kSecClass
, kSecClassCertificate
);
4373 CFDictionaryAddValue(query
, kSecReturnData
, kCFBooleanTrue
);
4374 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4375 if (combinedSearchList
) {
4376 CFDictionaryAddValue(query
, kSecMatchSearchList
, combinedSearchList
);
4377 CFRelease(combinedSearchList
);
4380 CFTypeRef results
= NULL
;
4381 if (normalizedIssuer
) {
4382 /* Look up certs whose subject is the same as this cert's issuer. */
4383 CFDictionaryAddValue(query
, kSecAttrSubject
, normalizedIssuer
);
4384 status
= SecItemCopyMatching_osx(query
, &results
);
4387 /* Cannot match anything without an issuer! */
4388 status
= errSecItemNotFound
;
4391 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4392 secitemlog(LOG_WARNING
, "SecItemCopyParentCertificates: %d", (int)status
);
4396 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4397 CFTypeID resultType
= (results
) ? CFGetTypeID(results
) : 0;
4398 if (resultType
== CFArrayGetTypeID()) {
4399 CFIndex index
, count
= CFArrayGetCount((CFArrayRef
)results
);
4400 for (index
= 0; index
< count
; index
++) {
4401 CFDataRef data
= (CFDataRef
) CFArrayGetValueAtIndex((CFArrayRef
)results
, index
);
4403 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
4405 CFArrayAppendValue(result
, cert
);
4410 } else if (resultType
== CFDataGetTypeID()) {
4411 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)results
);
4413 CFArrayAppendValue(result
, cert
);
4417 CFReleaseSafe(results
);
4418 CFReleaseSafe(normalizedIssuer
);
4421 parentCacheWrite(certificate
, result
);
4426 SecCertificateRef
SecItemCopyStoredCertificate(SecCertificateRef certificate
, void *context
)
4428 #pragma unused (context) /* for now; in future this can reference a container object */
4430 /* Certificates are unique by issuer and serial number. */
4431 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4432 CFDataRef serialNumber
= SecCertificateCopySerialNumber(certificate
, NULL
);
4433 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4435 CFDataRef serialNumber
= SecCertificateCopySerialNumber(certificate
);
4436 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4437 CFRetainSafe(normalizedIssuer
);
4440 const void *keys
[] = {
4444 kSecAttrSerialNumber
,
4448 kSecClassCertificate
,
4454 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 5, NULL
, NULL
);
4455 CFTypeRef result
= NULL
;
4457 OSStatus status
= SecItemCopyMatching_osx(query
, &result
);
4458 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4459 secitemlog(LOG_WARNING
, "SecItemCopyStoredCertificate: %d", (int)status
);
4460 CFReleaseNull(result
);
4462 CFReleaseSafe(query
);
4463 CFReleaseSafe(serialNumber
);
4464 CFReleaseSafe(normalizedIssuer
);
4466 return (SecCertificateRef
)result
;
4470 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4471 * and attempts to return a sanitized copy for passing to the underlying
4472 * platform-specific implementation code.
4474 * If iOSOut is true, one or more translations may apply:
4475 * - SecKeychain refs are removed, since there aren't multiple keychains
4476 * - SecPolicy refs are removed, since they can't be externalized
4477 * - SecAccess refs are removed, and potentially translated to entitlements
4479 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4480 * parameter errors due to strict input checks in secd, which only permits
4481 * these constants for calls to SecItemCopyMatching.
4483 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4484 * This permits a query to be reused for non-synchronizable items, or to
4485 * resolve a search based on a persistent item reference for iOS.
4487 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4488 * parameter errors due to strict input checks in secd, which do not permit
4489 * these constants for calls to SecItemUpdate.
4492 SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
4493 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
)
4495 CFMutableDictionaryRef result
= CFDictionaryCreateMutableCopy(NULL
, 0, inOSXDict
);
4496 if (result
== NULL
) {
4501 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4505 /* Match constants are only supported on iOS for SecItemCopyMatching,
4506 * and will generate an error if passed to other SecItem API functions;
4507 * on OS X, they're just ignored if not applicable for the context.
4509 CFDictionaryRemoveValue(result
, kSecMatchPolicy
);
4510 CFDictionaryRemoveValue(result
, kSecMatchItemList
);
4511 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4512 CFDictionaryRemoveValue(result
, kSecMatchIssuers
);
4513 CFDictionaryRemoveValue(result
, kSecMatchEmailAddressIfPresent
);
4514 CFDictionaryRemoveValue(result
, kSecMatchSubjectContains
);
4515 CFDictionaryRemoveValue(result
, kSecMatchCaseInsensitive
);
4516 CFDictionaryRemoveValue(result
, kSecMatchTrustedOnly
);
4517 CFDictionaryRemoveValue(result
, kSecMatchValidOnDate
);
4518 CFDictionaryRemoveValue(result
, kSecMatchLimit
);
4519 CFDictionaryRemoveValue(result
, kSecMatchLimitOne
);
4520 CFDictionaryRemoveValue(result
, kSecMatchLimitAll
);
4524 /* Return constants are not supported on iOS for SecItemUpdate,
4525 * where they will generate an error; on OS X, they're just ignored
4526 * if not applicable for the context.
4528 CFDictionaryRemoveValue(result
, kSecReturnData
);
4529 CFDictionaryRemoveValue(result
, kSecReturnAttributes
);
4530 CFDictionaryRemoveValue(result
, kSecReturnRef
);
4531 CFDictionaryRemoveValue(result
, kSecReturnPersistentRef
);
4535 /* Searching on data is not supported. */
4536 CFDictionaryRemoveValue(result
, kSecValueData
);
4540 /* Searching on access lists is not supported */
4541 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4545 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4546 * cannot specify a keychain search list on iOS
4548 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4550 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4551 * cannot specify a keychain on iOS
4553 CFDictionaryRemoveValue(result
, kSecUseKeychain
);
4555 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4556 * unless kSecAttrAccessGroup has already been specified.
4558 SecAccessRef access
= (SecAccessRef
) CFDictionaryGetValue(result
, kSecAttrAccess
);
4559 CFStringRef accessGroup
= (CFStringRef
) CFDictionaryGetValue(result
, kSecAttrAccessGroup
);
4560 if (access
!= NULL
&& accessGroup
== NULL
) {
4561 /* Translate "InternetAccounts" application group to an access group */
4562 if (errSecSuccess
== SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4563 /* The caller is a valid member of the application group. */
4564 CFStringRef groupName
= CFSTR("appleaccount");
4565 CFTypeRef value
= CFDictionaryGetValue(result
, kSecAttrAuthenticationType
);
4566 if (value
&& CFEqual(value
, kSecAttrAuthenticationTypeHTMLForm
)) {
4567 groupName
= CFSTR("com.apple.cfnetwork");
4569 CFDictionarySetValue(result
, kSecAttrAccessGroup
, groupName
);
4572 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4574 /* If item is specified by direct reference, and this is an iOS search,
4575 * replace it with a persistent reference, if it was recorded inside ItemImpl.
4577 CFTypeRef directRef
= CFDictionaryGetValue(result
, kSecValueRef
);
4578 if (directRef
!= NULL
) {
4579 CFTypeID typeID
= CFGetTypeID(directRef
);
4580 if ((typeID
!= SecKeyGetTypeID() || SecKeyIsCDSAKey((SecKeyRef
)directRef
)) &&
4581 (typeID
!= SecCertificateGetTypeID() || SecCertificateIsItemImplInstance((SecCertificateRef
)directRef
)) &&
4582 (typeID
!= SecIdentityGetTypeID())) {
4583 CFDataRef persistentRef
= _SecItemGetPersistentReference(directRef
);
4584 if (persistentRef
) {
4585 CFDictionarySetValue(result
, kSecValuePersistentRef
, persistentRef
);
4586 CFDictionaryRemoveValue(result
, kSecValueRef
);
4591 /* If item is specified by persistent reference, and this is an iOS search,
4592 * remove the synchronizable attribute as it will be rejected by secd.
4594 CFTypeRef persistentRef
= CFDictionaryGetValue(result
, kSecValuePersistentRef
);
4595 if (persistentRef
) {
4596 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4599 /* Remove kSecAttrModificationDate; this should never be used as criteria
4600 * for a search, or to add/modify an item. (If we are cloning an item
4601 * and want to keep its modification date, we don't call this function.)
4602 * It turns out that some clients are using the full attributes dictionary
4603 * returned by SecItemCopyMatching as a query to find the same item later,
4604 * which won't work once the item is updated.
4606 CFDictionaryRemoveValue(result
, kSecAttrModificationDate
);
4609 /* iOS doesn't add the class attribute, so we must do it here. */
4611 CFDictionarySetValue(result
, kSecClass
, itemClass
);
4613 /* Remove attributes which are not part of the OS X database schema. */
4614 CFDictionaryRemoveValue(result
, kSecAttrAccessible
);
4615 CFDictionaryRemoveValue(result
, kSecAttrAccessControl
);
4616 CFDictionaryRemoveValue(result
, kSecAttrAccessGroup
);
4617 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4618 CFDictionaryRemoveValue(result
, kSecAttrTombstone
);
4621 /* This attribute is consumed by the bridge itself. */
4622 CFDictionaryRemoveValue(result
, kSecAttrNoLegacy
);
4630 SecItemMergeResults(bool can_target_ios
, OSStatus status_ios
, CFTypeRef result_ios
,
4631 bool can_target_osx
, OSStatus status_osx
, CFTypeRef result_osx
,
4632 CFTypeRef
*result
) {
4633 // When querying both keychains and iOS keychain fails because of missing
4634 // entitlements, completely ignore iOS keychain result. This is to keep
4635 // backward compatibility with applications which know nothing about iOS keychain
4636 // and use SecItem API to access OSX keychain which does not need any entitlements.
4637 if (can_target_osx
&& can_target_ios
&& status_ios
== errSecMissingEntitlement
) {
4638 can_target_ios
= false;
4641 if (can_target_osx
&& can_target_ios
) {
4642 // If both keychains were targetted, examine returning statuses and decide what to do.
4643 if (status_ios
!= errSecSuccess
) {
4644 // iOS keychain failed to produce results because of some error, go with results from OSX keychain.
4645 AssignOrReleaseResult(result_osx
, result
);
4647 } else if (status_osx
!= errSecSuccess
) {
4648 if (status_osx
!= errSecItemNotFound
) {
4649 // OSX failed to produce results with some failure mode (else than not_found), but iOS produced results.
4650 // We have to either return OSX failure result and discard iOS results, or vice versa. For now, we just
4651 // ignore OSX error and return just iOS results.
4652 secitemlog(LOG_NOTICE
, "SecItemMergeResults: osx_result=%d, ignoring it, iOS succeeded fine", status_osx
);
4655 // OSX failed to produce results, but we have success from iOS keychain; go with results from iOS keychain.
4656 AssignOrReleaseResult(result_ios
, result
);
4657 return errSecSuccess
;
4659 // Both searches succeeded, merge results.
4660 if (result
!= NULL
) {
4661 CFTypeID id_osx
= (result_osx
) ? CFGetTypeID(result_osx
) : 0;
4662 CFTypeID id_ios
= (result_ios
) ? CFGetTypeID(result_ios
) : 0;
4663 CFTypeID id_array
= CFArrayGetTypeID();
4664 if ((id_osx
== id_array
) && (id_ios
== id_array
)) {
4665 // Fold the arrays into one.
4666 *result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4667 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_ios
,
4668 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_ios
)));
4669 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_osx
,
4670 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_osx
)));
4672 // Result type is not an array, so only one match can be returned.
4673 *result
= (id_ios
) ? result_ios
: result_osx
;
4674 CFRetainSafe(*result
);
4677 CFReleaseSafe(result_osx
);
4678 CFReleaseSafe(result_ios
);
4679 return errSecSuccess
;
4681 } else if (can_target_ios
) {
4682 // Only iOS keychain was targetted.
4683 AssignOrReleaseResult(result_ios
, result
);
4685 } else if (can_target_osx
) {
4686 // Only OSX keychain was targetted.
4687 AssignOrReleaseResult(result_osx
, result
);
4690 // Query could not run at all?
4696 ShouldTryUnlockKeybag(OSErr status
)
4698 static typeof(SASSessionStateForUser
) *soft_SASSessionStateForUser
= NULL
;
4699 static dispatch_once_t onceToken
;
4700 static void *framework
;
4702 if (status
!= errSecInteractionNotAllowed
)
4705 dispatch_once(&onceToken
, ^{
4706 framework
= dlopen("/System/Library/PrivateFrameworks/login.framework/login", RTLD_LAZY
);
4707 if (framework
== NULL
)
4709 soft_SASSessionStateForUser
= (typeof(soft_SASSessionStateForUser
)) dlsym(framework
, "SASSessionStateForUser");
4712 if (soft_SASSessionStateForUser
== NULL
)
4715 SessionAgentState sessionState
= soft_SASSessionStateForUser(getuid());
4716 if(sessionState
!= kSA_state_desktopshowing
)
4723 SecItemCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
)
4725 secitemlog(LOG_NOTICE
, "SecItemCopyMatching");
4729 secitemshow(query
, "SecItemCopyMatching query:");
4731 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4732 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4733 bool can_target_ios
, can_target_osx
;
4734 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
4735 if (status
!= errSecSuccess
) {
4739 if (can_target_ios
) {
4740 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4741 CFDictionaryGetValue(query
, kSecClass
), true, false, false, false, true, true);
4743 status_ios
= errSecParam
;
4746 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4747 if(ShouldTryUnlockKeybag(status_ios
)) {
4748 // The keybag is locked. Attempt to unlock it...
4749 secitemlog(LOG_WARNING
, "SecItemCopyMatching triggering SecurityAgent");
4750 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4751 CFReleaseNull(result_ios
);
4752 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4755 CFRelease(attrs_ios
);
4757 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_ios result: %d", status_ios
);
4760 if (can_target_osx
) {
4761 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4762 CFDictionaryGetValue(query
, kSecClass
), false, false, true, false, true, true);
4764 status_osx
= errSecParam
;
4767 status_osx
= SecItemCopyMatching_osx(attrs_osx
, &result_osx
);
4768 CFRelease(attrs_osx
);
4770 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_osx result: %d", status_osx
);
4773 status
= SecItemMergeResults(can_target_ios
, status_ios
, result_ios
,
4774 can_target_osx
, status_osx
, result_osx
, result
);
4775 secitemlog(LOG_NOTICE
, "SecItemCopyMatching result: %d", status
);
4780 SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
)
4782 secitemlog(LOG_NOTICE
, "SecItemAdd");
4789 secitemshow(attributes
, "SecItemAdd attrs:");
4791 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4792 bool can_target_ios
, can_target_osx
;
4793 OSStatus status
= SecItemCategorizeQuery(attributes
, can_target_ios
, can_target_osx
);
4794 if (status
!= errSecSuccess
) {
4798 // SecItemAdd cannot be really done on both keychains. In order to keep backward compatibility
4799 // with existing applications, we prefer to add items into legacy keychain and fallback
4800 // into iOS (modern) keychain only when the query is not suitable for legacy keychain.
4801 if (!can_target_osx
) {
4802 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(attributes
,
4803 NULL
, true, true, false, false, false, false);
4805 status
= errSecParam
;
4807 status
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4808 if(ShouldTryUnlockKeybag(status
)) {
4809 // The keybag is locked. Attempt to unlock it...
4810 secitemlog(LOG_WARNING
, "SecItemAdd triggering SecurityAgent");
4811 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(3)) {
4812 CFReleaseNull(result_ios
);
4813 status
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4816 CFRelease(attrs_ios
);
4818 secitemlog(LOG_NOTICE
, "SecItemAdd_ios result: %d", status
);
4819 AssignOrReleaseResult(result_ios
, result
);
4822 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(attributes
,
4823 NULL
, false, false, true, false, false, false);
4825 status
= errSecParam
;
4827 status
= SecItemAdd_osx(attrs_osx
, &result_osx
);
4828 CFRelease(attrs_osx
);
4830 secitemlog(LOG_NOTICE
, "SecItemAdd_osx result: %d", status
);
4831 AssignOrReleaseResult(result_osx
, result
);
4837 SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
)
4839 secitemlog(LOG_NOTICE
, "SecItemUpdate");
4840 if (!query
|| !attributesToUpdate
) {
4843 secitemshow(query
, "SecItemUpdate query:");
4844 secitemshow(attributesToUpdate
, "SecItemUpdate attrs:");
4846 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4847 bool can_target_ios
, can_target_osx
;
4848 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
4849 if (status
!= errSecSuccess
) {
4853 if (can_target_ios
) {
4854 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4855 CFDictionaryGetValue(query
, kSecClass
), true, true, false, true, true, true);
4857 status_ios
= errSecParam
;
4860 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate
)) {
4861 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
4862 if(ShouldTryUnlockKeybag(status_ios
)) {
4863 // The keybag is locked. Attempt to unlock it...
4864 secitemlog(LOG_WARNING
, "SecItemUpdate triggering SecurityAgent");
4865 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4866 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
4870 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
4871 if(ShouldTryUnlockKeybag(status_ios
)) {
4872 // The keybag is locked. Attempt to unlock it...
4873 secitemlog(LOG_WARNING
, "SecItemUpdate triggering SecurityAgent");
4874 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4875 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
4879 CFRelease(attrs_ios
);
4881 secitemlog(LOG_NOTICE
, "SecItemUpdate_ios result: %d", status_ios
);
4884 if (can_target_osx
) {
4885 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4886 CFDictionaryGetValue(query
, kSecClass
), false, false, true, true, true, true);
4888 status_osx
= errSecParam
;
4891 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate
))
4892 status_osx
= SecItemChangeSynchronizability(attrs_osx
, attributesToUpdate
, true);
4894 status_osx
= SecItemUpdate_osx(attrs_osx
, attributesToUpdate
);
4896 CFRelease(attrs_osx
);
4898 secitemlog(LOG_NOTICE
, "SecItemUpdate_osx result: %d", status_osx
);
4901 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
4902 can_target_osx
, status_osx
, NULL
, NULL
);
4903 secitemlog(LOG_NOTICE
, "SecItemUpdate result: %d", status
);
4908 SecItemDelete(CFDictionaryRef query
)
4910 secitemlog(LOG_NOTICE
, "SecItemDelete");
4914 secitemshow(query
, "SecItemDelete query:");
4916 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4917 bool can_target_ios
, can_target_osx
;
4918 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
4919 if (status
!= errSecSuccess
) {
4923 if (can_target_ios
) {
4924 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4925 NULL
, true, true, false, true, true, true);
4927 status_ios
= errSecParam
;
4929 status_ios
= SecItemDelete_ios(attrs_ios
);
4930 CFRelease(attrs_ios
);
4932 secitemlog(LOG_NOTICE
, "SecItemDelete_ios result: %d", status_ios
);
4935 if (can_target_osx
) {
4936 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4937 NULL
, false, false, true, true, true, true);
4939 status_osx
= errSecParam
;
4941 status_osx
= SecItemDelete_osx(attrs_osx
);
4942 CFRelease(attrs_osx
);
4944 secitemlog(LOG_NOTICE
, "SecItemDelete_osx result: %d", status_osx
);
4947 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
4948 can_target_osx
, status_osx
, NULL
, NULL
);
4949 secitemlog(LOG_NOTICE
, "SecItemCopyDelete result: %d", status
);
4954 SecItemUpdateTokenItems(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
4956 OSStatus status
= SecItemUpdateTokenItems_ios(tokenID
, tokenItemsAttributes
);
4957 if(ShouldTryUnlockKeybag(status
)) {
4958 // The keybag is locked. Attempt to unlock it...
4959 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4960 secitemlog(LOG_WARNING
, "SecItemUpdateTokenItems triggering SecurityAgent");
4961 status
= SecItemUpdateTokenItems_ios(tokenID
, tokenItemsAttributes
);
4964 secitemlog(LOG_NOTICE
, "SecItemUpdateTokenItems_ios result: %d", status
);
4969 SecItemCopyMatching_osx(
4970 CFDictionaryRef query
,
4973 if (!query
|| !result
)
4978 CFAllocatorRef allocator
= CFGetAllocator(query
);
4979 CFIndex matchCount
= 0;
4980 CFMutableArrayRef itemArray
= NULL
;
4981 SecKeychainItemRef item
= NULL
;
4982 SecIdentityRef identity
= NULL
;
4983 OSStatus tmpStatus
, status
= errSecSuccess
;
4985 // validate input query parameters and create the search reference
4986 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(query
, &status
);
4987 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
4989 // find the next match until we hit maxMatches, or no more matches found
4990 while ( !(!itemParams
->returnAllMatches
&& matchCount
>= itemParams
->maxMatches
) &&
4991 SecItemSearchCopyNext(itemParams
, (CFTypeRef
*)&item
) == errSecSuccess
) {
4993 if (FilterCandidateItem((CFTypeRef
*)&item
, itemParams
, &identity
))
4994 continue; // move on to next item
4996 ++matchCount
; // we have a match
4998 tmpStatus
= AddItemResults(item
, identity
, itemParams
, allocator
, &itemArray
, result
);
4999 if (tmpStatus
&& (status
== errSecSuccess
))
5007 CFRelease(identity
);
5012 if (status
== errSecSuccess
)
5013 status
= (matchCount
> 0) ? errSecSuccess
: errSecItemNotFound
;
5016 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5020 _FreeSecItemParams(itemParams
);
5026 SecItemCopyDisplayNames(
5028 CFArrayRef
*displayNames
)
5032 Required(displayNames
);
5034 return errSecUnimplemented
;
5040 CFDictionaryRef attributes
,
5048 CFAllocatorRef allocator
= CFGetAllocator(attributes
);
5049 CFMutableArrayRef itemArray
= NULL
;
5050 SecKeychainItemRef item
= NULL
;
5051 OSStatus tmpStatus
, status
= errSecSuccess
;
5053 // validate input attribute parameters
5054 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(attributes
, &status
);
5055 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
5057 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
5058 // since the private key should already be in a keychain by definition. We could support
5059 // this as a copy operation for the private key if a different keychain is specified,
5060 // but in any case it should try to add the certificate. See <rdar://8317887>.
5061 require_action(!itemParams
->returnIdentity
, error_exit
, status
= errSecItemInvalidValue
);
5063 if (itemParams
->useItems
== NULL
) {
5065 require_action(itemParams
->itemData
== NULL
|| CFGetTypeID(itemParams
->itemData
) == CFDataGetTypeID(),
5066 error_exit
, status
= errSecItemInvalidValue
);
5068 // create a single keychain item specified by the input attributes
5069 status
= SecKeychainItemCreateFromContent(itemParams
->itemClass
,
5070 itemParams
->attrList
,
5071 (itemParams
->itemData
) ? (UInt32
)CFDataGetLength(itemParams
->itemData
) : 0,
5072 (itemParams
->itemData
) ? CFDataGetBytePtrVoid(itemParams
->itemData
) : NULL
,
5073 itemParams
->keychain
,
5076 require_noerr(status
, error_exit
);
5078 // return results (if requested)
5080 itemParams
->maxMatches
= 1; // in case kSecMatchLimit was set to > 1
5081 tmpStatus
= AddItemResults(item
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5082 if (tmpStatus
&& (status
== errSecSuccess
))
5088 // add multiple items which are specified in the itemParams->useItems array.
5089 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
5090 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
5091 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
5093 OSStatus aggregateStatus
= errSecSuccess
;
5094 CFIndex ix
, count
= CFArrayGetCount(itemParams
->useItems
);
5095 itemParams
->maxMatches
= (count
> 1) ? (int)count
: 2; // force results to always be returned as an array
5096 for (ix
=0; ix
< count
; ix
++) {
5097 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->useItems
, ix
);
5099 if (SecCertificateGetTypeID() == CFGetTypeID(anItem
)) {
5100 // SecCertificateRef item
5101 tmpStatus
= SecCertificateAddToKeychain((SecCertificateRef
)anItem
, itemParams
->keychain
);
5102 if (!tmpStatus
&& result
) {
5103 tmpStatus
= AddItemResults((SecKeychainItemRef
)anItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5105 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5107 else if (SecKeyGetTypeID() == CFGetTypeID(anItem
)) {
5109 SecKeychainRef itemKeychain
= NULL
;
5110 tmpStatus
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)anItem
, &itemKeychain
);
5111 if (tmpStatus
== errSecSuccess
) {
5112 // key was in a keychain, so we can attempt to copy it
5113 SecKeychainItemRef itemCopy
= NULL
;
5114 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5115 if (!tmpStatus
&& result
) {
5116 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5119 CFRelease(itemCopy
);
5123 // key was not in any keychain, so must be imported
5124 SecKeychainItemRef keyItem
= NULL
;
5125 tmpStatus
= _ImportKey((SecKeyRef
)anItem
, itemParams
->keychain
, itemParams
->access
, itemParams
->attrList
, &keyItem
);
5126 if (!tmpStatus
&& result
) {
5127 tmpStatus
= AddItemResults(keyItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5134 CFRelease(itemKeychain
);
5136 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5138 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem
)) {
5139 // SecKeychainItemRef item
5140 SecKeychainItemRef itemCopy
= NULL
;
5141 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5142 if (!tmpStatus
&& result
) {
5143 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5146 CFRelease(itemCopy
);
5148 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5150 else if (CFDataGetTypeID() == CFGetTypeID(anItem
)) {
5151 // CFDataRef item (persistent reference)
5152 SecKeychainItemRef realItem
= NULL
;
5153 tmpStatus
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
);
5154 if (tmpStatus
== errSecSuccess
) {
5155 // persistent reference resolved to a keychain item, so we can attempt to copy it
5156 SecKeychainItemRef itemCopy
= NULL
;
5157 tmpStatus
= SecKeychainItemCreateCopy(realItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5158 if (!tmpStatus
&& result
) {
5159 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5162 CFRelease(itemCopy
);
5166 CFRelease(realItem
);
5168 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5171 } // end of itemList array loop
5172 status
= aggregateStatus
;
5173 } // end processing multiple items
5176 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5180 _FreeSecItemParams(itemParams
);
5187 CFDictionaryRef query
,
5188 CFDictionaryRef attributesToUpdate
)
5190 if (!query
|| !attributesToUpdate
)
5193 // run the provided query to get a list of items to update
5194 CFTypeRef results
= NULL
;
5195 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5196 if (status
!= errSecSuccess
)
5197 return status
; // nothing was matched, or the query was bad
5199 CFArrayRef items
= NULL
;
5200 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5201 items
= (CFArrayRef
) results
;
5204 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5208 OSStatus result
= errSecSuccess
;
5209 CFIndex ix
, count
= CFArrayGetCount(items
);
5210 for (ix
=0; ix
< count
; ix
++) {
5211 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5213 status
= _UpdateKeychainItem(anItem
, attributesToUpdate
);
5214 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);
5226 CFDictionaryRef query
)
5231 // run the provided query to get a list of items to delete
5232 CFTypeRef results
= NULL
;
5233 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5234 if (status
!= errSecSuccess
)
5235 return status
; // nothing was matched, or the query was bad
5237 CFArrayRef items
= NULL
;
5238 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5239 items
= (CFArrayRef
) results
;
5242 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5246 OSStatus result
= errSecSuccess
;
5247 CFIndex ix
, count
= CFArrayGetCount(items
);
5248 for (ix
=0; ix
< count
; ix
++) {
5249 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5251 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
)) {
5252 status
= _DeleteIdentity((SecIdentityRef
)anItem
);
5255 status
= _DeleteKeychainItem(anItem
);
5257 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);