2 * Copyright (c) 2006-2019 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 <Security/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 <Security/SecCertificatePriv.h>
32 #include <sys/param.h>
33 #include "cssmdatetime.h"
34 #include <Security/SecItem.h>
35 #include <Security/SecItemPriv.h>
36 #include <Security/SecIdentitySearchPriv.h>
37 #include <Security/SecKeychainPriv.h>
38 #include <Security/SecCertificatePriv.h>
39 #include <Security/SecPolicyPriv.h>
40 #include "TrustAdditions.h"
41 #include "TrustSettingsSchema.h"
42 #include <Security/SecTrustPriv.h>
43 #include "utilities/array_size.h"
44 #include "utilities/SecCFWrappers.h"
45 #include "LegacyAPICounts.h"
47 #include <AssertMacros.h>
51 #include <Security/SecTrustedApplication.h>
52 #include <Security/SecTrustedApplicationPriv.h>
53 #include <Security/SecCode.h>
54 #include <Security/SecCodePriv.h>
55 #include <Security/SecRequirement.h>
57 #include <login/SessionAgentCom.h>
58 #include <login/SessionAgentStatusCom.h>
59 #include <os/activity.h>
60 #include <CoreFoundation/CFPriv.h>
63 const uint8_t kUUIDStringLength
= 36;
65 OSStatus
SecItemAdd_osx(CFDictionaryRef attributes
, CFTypeRef
*result
);
66 OSStatus
SecItemCopyMatching_osx(CFDictionaryRef query
, CFTypeRef
*result
);
67 OSStatus
SecItemUpdate_osx(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
68 OSStatus
SecItemDelete_osx(CFDictionaryRef query
);
71 OSStatus
SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
);
72 OSStatus
SecItemCopyMatching_ios(CFDictionaryRef query
, CFTypeRef
*result
);
73 OSStatus
SecItemUpdate_ios(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
74 OSStatus
SecItemDelete_ios(CFDictionaryRef query
);
76 OSStatus
SecItemValidateAppleApplicationGroupAccess(CFStringRef group
);
77 CFDictionaryRef
SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
78 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
);
80 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
,
81 long long int *return_rowid
, CFDictionaryRef
*return_token_attrs
);
84 static Boolean
SecItemSynchronizable(CFDictionaryRef query
);
85 static CFArrayRef
_CopyMatchingIssuers(CFArrayRef issuers
);
87 static void secitemlog(int priority
, const char *format
, ...)
92 if (priority
< LOG_NOTICE
) // log warnings and errors
96 va_start(list
, format
);
97 vsyslog(priority
, format
, list
);
102 static void secitemshow(CFTypeRef obj
, const char *context
)
105 CFStringRef desc
= CFCopyDescription(obj
);
108 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc
), kCFStringEncodingUTF8
) + 1;
109 char* buffer
= (char*) malloc(length
);
111 Boolean converted
= CFStringGetCString(desc
, buffer
, length
, kCFStringEncodingUTF8
);
113 const char *prefix
= (context
) ? context
: "";
114 const char *separator
= (context
) ? " " : "";
115 secitemlog(LOG_NOTICE
, "%s%s%s", prefix
, separator
, buffer
);
124 #define CFDataGetBytePtrVoid CFDataGetBytePtr
126 #pragma mark SecItem private utility functions
128 /******************************************************************************/
130 struct ProtocolAttributeInfo
{
131 const CFStringRef
*protocolValue
;
132 SecProtocolType protocolType
;
135 static ProtocolAttributeInfo gProtocolTypes
[] = {
136 { &kSecAttrProtocolFTP
, kSecProtocolTypeFTP
},
137 { &kSecAttrProtocolFTPAccount
, kSecProtocolTypeFTPAccount
},
138 { &kSecAttrProtocolHTTP
, kSecProtocolTypeHTTP
},
139 { &kSecAttrProtocolIRC
, kSecProtocolTypeIRC
},
140 { &kSecAttrProtocolNNTP
, kSecProtocolTypeNNTP
},
141 { &kSecAttrProtocolPOP3
, kSecProtocolTypePOP3
},
142 { &kSecAttrProtocolSMTP
, kSecProtocolTypeSMTP
},
143 { &kSecAttrProtocolSOCKS
, kSecProtocolTypeSOCKS
},
144 { &kSecAttrProtocolIMAP
, kSecProtocolTypeIMAP
},
145 { &kSecAttrProtocolLDAP
, kSecProtocolTypeLDAP
},
146 { &kSecAttrProtocolAppleTalk
, kSecProtocolTypeAppleTalk
},
147 { &kSecAttrProtocolAFP
, kSecProtocolTypeAFP
},
148 { &kSecAttrProtocolTelnet
, kSecProtocolTypeTelnet
},
149 { &kSecAttrProtocolSSH
, kSecProtocolTypeSSH
},
150 { &kSecAttrProtocolFTPS
, kSecProtocolTypeFTPS
},
151 { &kSecAttrProtocolHTTPS
, kSecProtocolTypeHTTPS
},
152 { &kSecAttrProtocolHTTPProxy
, kSecProtocolTypeHTTPProxy
},
153 { &kSecAttrProtocolHTTPSProxy
, kSecProtocolTypeHTTPSProxy
},
154 { &kSecAttrProtocolFTPProxy
, kSecProtocolTypeFTPProxy
},
155 { &kSecAttrProtocolSMB
, kSecProtocolTypeSMB
},
156 { &kSecAttrProtocolRTSP
, kSecProtocolTypeRTSP
},
157 { &kSecAttrProtocolRTSPProxy
, kSecProtocolTypeRTSPProxy
},
158 { &kSecAttrProtocolDAAP
, kSecProtocolTypeDAAP
},
159 { &kSecAttrProtocolEPPC
, kSecProtocolTypeEPPC
},
160 { &kSecAttrProtocolIPP
, kSecProtocolTypeIPP
},
161 { &kSecAttrProtocolNNTPS
, kSecProtocolTypeNNTPS
},
162 { &kSecAttrProtocolLDAPS
, kSecProtocolTypeLDAPS
},
163 { &kSecAttrProtocolTelnetS
, kSecProtocolTypeTelnetS
},
164 { &kSecAttrProtocolIMAPS
, kSecProtocolTypeIMAPS
},
165 { &kSecAttrProtocolIRCS
, kSecProtocolTypeIRCS
},
166 { &kSecAttrProtocolPOP3S
, kSecProtocolTypePOP3S
}
169 static const int kNumberOfProtocolTypes
= sizeof(gProtocolTypes
) / sizeof(ProtocolAttributeInfo
);
172 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
174 static SecProtocolType
175 _SecProtocolTypeForSecAttrProtocol(
178 SecProtocolType result
= kSecProtocolTypeAny
;
180 if (protocol
!= NULL
) {
182 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
183 if (CFEqual(protocol
, *(gProtocolTypes
[count
].protocolValue
))) {
184 result
= gProtocolTypes
[count
].protocolType
;
194 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
197 _SecAttrProtocolForSecProtocolType(
198 SecProtocolType protocolType
)
200 CFTypeRef result
= NULL
;
202 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
203 if (gProtocolTypes
[count
].protocolType
== protocolType
) {
204 result
= *(gProtocolTypes
[count
].protocolValue
);
213 /******************************************************************************/
215 struct AuthenticationAttributeInfo
{
216 const CFStringRef
*authValue
;
217 SecAuthenticationType authType
;
220 static AuthenticationAttributeInfo gAuthTypes
[] = {
221 { &kSecAttrAuthenticationTypeNTLM
, kSecAuthenticationTypeNTLM
},
222 { &kSecAttrAuthenticationTypeMSN
, kSecAuthenticationTypeMSN
},
223 { &kSecAttrAuthenticationTypeDPA
, kSecAuthenticationTypeDPA
},
224 { &kSecAttrAuthenticationTypeRPA
, kSecAuthenticationTypeRPA
},
225 { &kSecAttrAuthenticationTypeHTTPBasic
, kSecAuthenticationTypeHTTPBasic
},
226 { &kSecAttrAuthenticationTypeHTTPDigest
, kSecAuthenticationTypeHTTPDigest
},
227 { &kSecAttrAuthenticationTypeHTMLForm
, kSecAuthenticationTypeHTMLForm
},
228 { &kSecAttrAuthenticationTypeDefault
, kSecAuthenticationTypeDefault
}
231 static const int kNumberOfAuthenticationTypes
= sizeof(gAuthTypes
) / sizeof(AuthenticationAttributeInfo
);
234 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
235 * SecAttrAuthenticationType to a SecAuthenticationType.
237 static SecAuthenticationType
238 _SecAuthenticationTypeForSecAttrAuthenticationType(
239 CFTypeRef authenticationType
)
241 SecAuthenticationType result
= kSecAuthenticationTypeAny
;
243 if (authenticationType
!= NULL
) {
245 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
246 if (CFEqual(authenticationType
, *(gAuthTypes
[count
].authValue
))) {
247 result
= gAuthTypes
[count
].authType
;
257 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
258 * to a SecAttrAuthenticationType.
261 _SecAttrAuthenticationTypeForSecAuthenticationType(
262 SecAuthenticationType authenticationType
)
264 CFTypeRef result
= NULL
;
266 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
267 if (gAuthTypes
[count
].authType
== authenticationType
) {
268 result
= *(gAuthTypes
[count
].authValue
);
277 /******************************************************************************/
279 struct KeyAlgorithmInfo
{
280 const CFStringRef
*keyType
;
284 static KeyAlgorithmInfo gKeyTypes
[] = {
285 { &kSecAttrKeyTypeRSA
, CSSM_ALGID_RSA
},
286 { &kSecAttrKeyTypeDSA
, CSSM_ALGID_DSA
},
287 { &kSecAttrKeyTypeAES
, CSSM_ALGID_AES
},
288 { &kSecAttrKeyTypeDES
, CSSM_ALGID_DES
},
289 { &kSecAttrKeyType3DES
, CSSM_ALGID_3DES
},
290 { &kSecAttrKeyTypeRC4
, CSSM_ALGID_RC4
},
291 { &kSecAttrKeyTypeRC2
, CSSM_ALGID_RC2
},
292 { &kSecAttrKeyTypeCAST
, CSSM_ALGID_CAST
},
293 { &kSecAttrKeyTypeECDSA
, CSSM_ALGID_ECDSA
},
294 { &kSecAttrKeyTypeEC
, CSSM_ALGID_ECDSA
}
297 static const int kNumberOfKeyTypes
= sizeof(gKeyTypes
) / sizeof (KeyAlgorithmInfo
);
300 static UInt32
_SecAlgorithmTypeFromSecAttrKeyType(
301 CFTypeRef keyTypeRef
)
303 UInt32 keyAlgValue
= 0;
304 if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef
))
308 for (ix
=0; ix
<kNumberOfKeyTypes
; ix
++) {
309 if (CFEqual(keyTypeRef
, *(gKeyTypes
[ix
].keyType
))) {
310 keyAlgValue
= gKeyTypes
[ix
].keyValue
;
315 //%%%TODO try to convert the input string to a number here
321 enum ItemRepresentation
323 kStringRepresentation
,
325 kNumberRepresentation
,
326 kBooleanRepresentation
,
331 struct InternalAttributeListInfo
334 const CFStringRef
*newItemType
;
335 ItemRepresentation itemRepresentation
;
339 static InternalAttributeListInfo gGenericPasswordAttributes
[] =
341 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
342 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
343 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
344 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
345 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
346 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
347 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
348 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
349 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
350 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
351 { kSecServiceItemAttr
, &kSecAttrService
, kStringRepresentation
},
352 { kSecGenericItemAttr
, &kSecAttrGeneric
, kDataRepresentation
}
355 static const int kNumberOfGenericPasswordAttributes
= sizeof(gGenericPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
358 static InternalAttributeListInfo gInternetPasswordAttributes
[] =
360 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
361 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
362 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
363 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
364 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
365 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
366 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
367 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
368 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
369 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
370 { kSecSecurityDomainItemAttr
, &kSecAttrSecurityDomain
, kStringRepresentation
},
371 { kSecServerItemAttr
, &kSecAttrServer
, kStringRepresentation
},
372 { kSecAuthenticationTypeItemAttr
, &kSecAttrAuthenticationType
, kStringRepresentation
}, // maps from UInt32 value to string constant
373 { kSecPortItemAttr
, &kSecAttrPort
, kNumberRepresentation
},
374 { kSecPathItemAttr
, &kSecAttrPath
, kStringRepresentation
}
377 static const int kNumberOfInternetPasswordAttributes
= sizeof(gInternetPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
380 static InternalAttributeListInfo gCertificateAttributes
[] =
382 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
383 { kSecSubjectItemAttr
, &kSecAttrSubject
, kDataRepresentation
},
384 { kSecIssuerItemAttr
, &kSecAttrIssuer
, kDataRepresentation
},
385 { kSecSerialNumberItemAttr
, &kSecAttrSerialNumber
, kDataRepresentation
},
386 { kSecPublicKeyHashItemAttr
, &kSecAttrPublicKeyHash
, kDataRepresentation
},
387 { kSecSubjectKeyIdentifierItemAttr
, &kSecAttrSubjectKeyID
, kDataRepresentation
},
388 { kSecCertTypeItemAttr
, &kSecAttrCertificateType
, kDataRepresentation
},
389 { kSecCertEncodingItemAttr
, &kSecAttrCertificateEncoding
, kDataRepresentation
}
392 static const int kNumberOfCertificateAttributes
= sizeof(gCertificateAttributes
) / sizeof(InternalAttributeListInfo
);
395 static InternalAttributeListInfo gKeyAttributes
[] =
397 { kSecKeyKeyClass
, &kSecAttrKeyClass
, kStringRepresentation
}, // key class maps from UInt32 value to string constant
398 { kSecKeyPrintName
, &kSecAttrLabel
, kStringRepresentation
}, // note that "print name" maps to the user-visible label
399 // { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
400 { kSecKeyPermanent
, &kSecAttrIsPermanent
, kBooleanRepresentation
},
401 // { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
402 // { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
403 { 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
404 { kSecKeyApplicationTag
, &kSecAttrApplicationTag
, kDataRepresentation
},
405 // { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
406 { kSecKeyKeyType
, &kSecAttrKeyType
, kStringRepresentation
}, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
407 { kSecKeyKeySizeInBits
, &kSecAttrKeySizeInBits
, kNumberRepresentation
},
408 { kSecKeyEffectiveKeySize
, &kSecAttrEffectiveKeySize
, kNumberRepresentation
},
409 // { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
410 // { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
411 // { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
412 // { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
413 // { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
414 // { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
415 { kSecKeyEncrypt
, &kSecAttrCanEncrypt
, kBooleanRepresentation
},
416 { kSecKeyDecrypt
, &kSecAttrCanDecrypt
, kBooleanRepresentation
},
417 { kSecKeyDerive
, &kSecAttrCanDerive
, kBooleanRepresentation
},
418 { kSecKeySign
, &kSecAttrCanSign
, kBooleanRepresentation
},
419 { kSecKeyVerify
, &kSecAttrCanVerify
, kBooleanRepresentation
},
420 // { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
421 // { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
422 { kSecKeyWrap
, &kSecAttrCanWrap
, kBooleanRepresentation
},
423 { kSecKeyUnwrap
, &kSecAttrCanUnwrap
, kBooleanRepresentation
}
426 static const int kNumberOfKeyAttributes
= sizeof(gKeyAttributes
) / sizeof(InternalAttributeListInfo
);
429 static void* CloneDataByType(ItemRepresentation type
, CFTypeRef value
, UInt32
& length
)
433 case kStringRepresentation
:
435 if (CFStringGetTypeID() != CFGetTypeID(value
)) {
439 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
440 char* buffer
= (char*) malloc(maxLength
);
441 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
443 length
= (UInt32
)strlen(buffer
);
453 case kDataRepresentation
:
455 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
456 // We may have a string here, since the key label may be a GUID for the symmetric keys
457 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
458 char* buffer
= (char*) malloc(maxLength
);
459 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
461 length
= (UInt32
)strlen(buffer
);
471 if (CFDataGetTypeID() != CFGetTypeID(value
)) {
475 length
= (UInt32
)CFDataGetLength((CFDataRef
) value
);
476 uint8_t* buffer
= (uint8_t*) malloc(length
);
477 CFDataGetBytes((CFDataRef
) value
, CFRangeMake(0, length
), buffer
);
481 case kNumberRepresentation
:
483 if (CFNumberGetTypeID() != CFGetTypeID(value
)) {
487 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
488 Boolean converted
= CFNumberGetValue((CFNumberRef
) value
, kCFNumberSInt32Type
, buffer
);
490 length
= sizeof(uint32_t);
500 case kBooleanRepresentation
:
502 if (CFBooleanGetTypeID() != CFGetTypeID(value
)) {
506 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
507 *buffer
= (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
508 length
= sizeof(uint32_t);
512 case kDateRepresentation
:
514 if (CFDateGetTypeID() != CFGetTypeID(value
)) {
518 char* buffer
= (char*) calloc(1, 32); // max length of a CSSM date string
519 CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef
) value
, buffer
);
520 length
= (UInt32
)strlen(buffer
);
534 _ConvertNewFormatToOldFormat(
535 CFAllocatorRef allocator
,
536 const InternalAttributeListInfo
* info
,
538 CFDictionaryRef dictionaryRef
,
539 SecKeychainAttributeList
* &attrList
542 // make storage to extract the dictionary items
543 CFIndex itemsInDictionary
= CFDictionaryGetCount(dictionaryRef
);
544 if (itemsInDictionary
> 10000) {
548 // get the keychain attributes array from the data item
549 // here's the problem. On the one hand, we have a dictionary that is purported to contain
550 // attributes for our type. On the other hand, the dictionary may contain items we don't support,
551 // and we therefore don't know how many attributes we will have unless we count them first
554 attrList
= (SecKeychainAttributeList
*) calloc(1, sizeof(SecKeychainAttributeList
));
556 std::vector
<CFTypeRef
> keys(itemsInDictionary
);
557 std::vector
<CFTypeRef
> values(itemsInDictionary
);
559 CFDictionaryGetKeysAndValues(dictionaryRef
, keys
.data(), values
.data());
561 // count the number of items we are interested in
565 // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
566 // we don't pay the price for this twice
567 std::vector
<SecKeychainAttrType
> tags(itemsInDictionary
);
568 std::vector
<ItemRepresentation
> types(itemsInDictionary
);
570 for (i
= 0; i
< itemsInDictionary
; ++i
)
572 CFTypeRef key
= keys
[i
];
575 for (j
= 0; j
< infoNumItems
; ++j
)
577 if (CFEqual(*(info
[j
].newItemType
), key
))
579 tags
[i
] = info
[j
].oldItemType
;
580 types
[i
] = info
[j
].itemRepresentation
;
586 if (j
>= infoNumItems
)
588 // if we got here, we aren't interested in this item.
593 // now we can make the result array
594 attrList
->count
= (UInt32
)count
;
597 attrList
->attr
= NULL
;
599 attrList
->attr
= (SecKeychainAttribute
*) calloc(count
, sizeof(SecKeychainAttribute
));
601 // fill out the array
602 int resultPointer
= 0;
603 for (i
= 0; i
< itemsInDictionary
; ++i
)
605 if (values
[i
] != NULL
)
607 attrList
->attr
[resultPointer
].tag
= tags
[i
];
609 // we have to clone the data pointer. The caller will need to make sure to throw these away
610 // with _FreeAttrList when it is done...
611 attrList
->attr
[resultPointer
].data
= CloneDataByType(types
[i
], values
[i
], attrList
->attr
[resultPointer
].length
);
617 return errSecSuccess
;
623 _ConvertOldFormatToNewFormat(
624 CFAllocatorRef allocator
,
625 const InternalAttributeListInfo
* info
,
627 SecKeychainItemRef itemRef
,
628 CFMutableDictionaryRef
& dictionaryRef
)
630 SecKeychainAttributeList list
;
631 list
.count
= infoNumItems
;
632 list
.attr
= (SecKeychainAttribute
*) calloc(infoNumItems
, sizeof(SecKeychainAttribute
));
634 // fill out the array. We only need to fill in the tags, since calloc zeros what it returns
636 for (i
= 0; i
< infoNumItems
; ++i
)
638 list
.attr
[i
].tag
= info
[i
].oldItemType
;
641 OSStatus result
= SecKeychainItemCopyContent(itemRef
, NULL
, &list
, NULL
, NULL
);
642 if (result
!= errSecSuccess
)
644 dictionaryRef
= NULL
;
649 // create the dictionary
650 dictionaryRef
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
653 for (i
= 0; i
< infoNumItems
; ++i
)
655 if (list
.attr
[i
].data
== NULL
)
658 switch (info
[i
].itemRepresentation
)
660 case kStringRepresentation
:
662 CFStringRef stringRef
;
663 if (info
[i
].oldItemType
== kSecKeyKeyClass
) {
664 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
665 uint32_t keyRecordValue
= *((uint32_t*)list
.attr
[i
].data
);
666 bool retainString
= true;
667 switch (keyRecordValue
) {
668 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
669 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
671 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
672 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
674 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
675 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
678 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyRecordValue
);
679 retainString
= false;
683 if (retainString
) CFRetain(stringRef
);
684 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
685 CFRelease(stringRef
);
688 else if (info
[i
].oldItemType
== kSecKeyKeyType
) {
689 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
690 uint32_t keyAlgValue
= *((uint32_t*)list
.attr
[i
].data
);
691 bool retainString
= true;
692 switch (keyAlgValue
) {
693 case CSSM_ALGID_RSA
:
694 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
696 case CSSM_ALGID_DSA
:
697 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
699 case CSSM_ALGID_AES
:
700 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
702 case CSSM_ALGID_DES
:
703 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
705 case CSSM_ALGID_3DES
:
706 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
708 case CSSM_ALGID_RC4
:
709 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
711 case CSSM_ALGID_RC2
:
712 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
714 case CSSM_ALGID_CAST
:
715 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
717 case CSSM_ALGID_ECDSA
:
718 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
721 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyAlgValue
);
722 retainString
= false;
726 if (retainString
) CFRetain(stringRef
);
727 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
728 CFRelease(stringRef
);
732 // normal case: attribute contains a string
733 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
734 if (stringRef
== NULL
)
735 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
736 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
737 CFRelease(stringRef
);
742 case kDataRepresentation
:
744 if ((info
[i
].oldItemType
== kSecKeyLabel
) && (list
.attr
[i
].length
== kUUIDStringLength
)) {
745 // It's possible that there could be a string here because the key label may have a UUID
746 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
747 if (stringRef
== NULL
)
748 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
749 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
750 CFRelease(stringRef
);
753 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*) list
.attr
[i
].data
, list
.attr
[i
].length
);
755 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
756 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dataRef
);
761 case kNumberRepresentation
:
763 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, list
.attr
[i
].data
);
764 if (numberRef
== NULL
)
765 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
766 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), numberRef
);
767 CFRelease(numberRef
);
771 case kBooleanRepresentation
:
773 uint32_t value
= *((uint32_t*)list
.attr
[i
].data
);
774 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
775 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), boolRef
);
779 case kDateRepresentation
:
781 CFDateRef dateRef
= NULL
;
782 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list
.attr
[i
].data
, list
.attr
[i
].length
, &dateRef
);
784 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
785 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dateRef
);
793 SecKeychainItemFreeContent(&list
, NULL
);
803 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
804 * attributes of item.
807 _CreateAttributesDictionaryFromGenericPasswordItem(
808 CFAllocatorRef allocator
,
809 SecKeychainItemRef item
,
810 CFDictionaryRef
*dictionary
)
812 // do the basic allocations
813 CFMutableDictionaryRef dict
= NULL
;
814 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, item
, dict
);
815 if (result
== errSecSuccess
) // did we complete OK
817 CFDictionaryAddValue(dict
, kSecClass
, kSecClassGenericPassword
);
828 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
829 * attributes of item.
832 _CreateAttributesDictionaryFromCertificateItem(
833 CFAllocatorRef allocator
,
834 SecKeychainItemRef item
,
835 CFDictionaryRef
*dictionary
)
837 // do the basic allocations
838 CFMutableDictionaryRef dict
= NULL
;
839 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gCertificateAttributes
, kNumberOfCertificateAttributes
, item
, dict
);
840 if (result
== errSecSuccess
) // did we complete OK
842 CFDictionaryAddValue(dict
, kSecClass
, kSecClassCertificate
);
847 return errSecSuccess
;
851 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
852 * attributes of item.
855 _CreateAttributesDictionaryFromKeyItem(
856 CFAllocatorRef allocator
,
857 SecKeychainItemRef item
,
858 CFDictionaryRef
*dictionary
)
861 //%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
862 // Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
863 // SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
866 goto error_exit
; // unable to get the attribute info (i.e. database schema)
869 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
871 // do the basic allocations
872 CFMutableDictionaryRef dict
= NULL
;
873 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gKeyAttributes
, kNumberOfKeyAttributes
, item
, dict
);
874 if (result
== errSecSuccess
) // did we complete OK
876 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
881 return errSecSuccess
;
884 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
886 SecItemClass itemClass
= (SecItemClass
) 0;
888 SecKeychainAttributeList
*attrList
= NULL
;
889 SecKeychainAttributeInfo
*info
= NULL
;
890 SecKeychainRef keychain
= NULL
;
892 OSStatus status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
894 goto error_exit
; // item must have an itemClass
899 case kSecInternetPasswordItemClass
:
900 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
902 case kSecGenericPasswordItemClass
:
903 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
905 case 'ashp': /* kSecAppleSharePasswordItemClass */
906 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
913 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
915 goto error_exit
; // item must have a keychain, so we can get the attribute info for it
918 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
920 goto error_exit
; // unable to get the attribute info (i.e. database schema)
923 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
925 goto error_exit
; // unable to get the attribute info (i.e. database schema)
928 for (ix
= 0; ix
< info
->count
; ++ix
)
930 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
931 if (!attribute
->length
&& !attribute
->data
)
934 UInt32 j
, count
= kNumberOfKeyAttributes
;
935 InternalAttributeListInfo
*intInfo
= NULL
;
936 for (j
=0; j
<count
; j
++) {
937 if (gKeyAttributes
[j
].oldItemType
== info
->tag
[ix
]) {
938 intInfo
= &gKeyAttributes
[j
];
945 switch (intInfo
->itemRepresentation
)
947 case kStringRepresentation
:
949 CFStringRef stringRef
;
950 if (intInfo
->oldItemType
== kSecKeyKeyClass
) {
951 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
952 UInt32 keyRecordValue
= *((UInt32
*)attribute
->data
);
953 bool retainString
= true;
954 switch (keyRecordValue
) {
955 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
956 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
958 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
959 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
961 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
962 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
965 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyRecordValue
);
966 retainString
= false;
970 if (retainString
) CFRetain(stringRef
);
971 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
972 CFRelease(stringRef
);
975 else if (intInfo
->oldItemType
== kSecKeyKeyType
) {
976 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
977 UInt32 keyAlgValue
= *((UInt32
*)attribute
->data
);
978 bool retainString
= true;
979 switch (keyAlgValue
) {
980 case CSSM_ALGID_RSA
:
981 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
983 case CSSM_ALGID_DSA
:
984 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
986 case CSSM_ALGID_AES
:
987 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
989 case CSSM_ALGID_DES
:
990 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
992 case CSSM_ALGID_3DES
:
993 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
995 case CSSM_ALGID_RC4
:
996 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
998 case CSSM_ALGID_RC2
:
999 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
1001 case CSSM_ALGID_CAST
:
1002 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
1004 case CSSM_ALGID_ECDSA
:
1005 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
1008 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyAlgValue
);
1009 retainString
= false;
1013 if (retainString
) CFRetain(stringRef
);
1014 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1015 CFRelease(stringRef
);
1019 // normal case: attribute contains a string
1020 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1021 if (stringRef
== NULL
)
1022 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1023 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1024 CFRelease(stringRef
);
1029 case kDataRepresentation
:
1031 if ((intInfo
->oldItemType
== kSecKeyLabel
) && (attribute
->length
== kUUIDStringLength
)) {
1032 // It's possible that there could be a string here because the key label may have a UUID
1033 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1034 if (stringRef
== NULL
)
1035 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1036 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1037 CFRelease(stringRef
);
1041 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)attribute
->data
, attribute
->length
);
1042 if (dataRef
== NULL
)
1043 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
1044 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dataRef
);
1049 case kNumberRepresentation
:
1051 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attribute
->data
);
1052 if (numberRef
== NULL
)
1053 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
1054 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), numberRef
);
1055 CFRelease(numberRef
);
1059 case kBooleanRepresentation
:
1061 UInt32 value
= *((UInt32
*)attribute
->data
);
1062 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1063 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), boolRef
);
1067 case kDateRepresentation
:
1069 //%%% FIXME need to convert from a CSSM date string to a CFDateRef here
1070 CFDateRef dateRef
= NULL
;
1071 if (dateRef
== NULL
)
1072 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
1073 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dateRef
);
1080 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
1085 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
1088 SecKeychainFreeAttributeInfo(info
);
1091 CFRelease(keychain
);
1100 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
1101 * attributes of item.
1104 _CreateAttributesDictionaryFromInternetPasswordItem(
1105 CFAllocatorRef allocator
,
1106 SecKeychainItemRef item
,
1107 CFDictionaryRef
*dictionary
)
1110 SecKeychainAttribute attr
[] = {
1111 { kSecServerItemAttr
, 0, NULL
}, /* [0] server */
1112 { kSecSecurityDomainItemAttr
, 0, NULL
}, /* [1] securityDomain */
1113 { kSecAccountItemAttr
, 0, NULL
}, /* [2] account */
1114 { kSecPathItemAttr
, 0, NULL
}, /* [3] path */
1115 { kSecPortItemAttr
, 0, NULL
}, /* [4] port */
1116 { kSecProtocolItemAttr
, 0, NULL
}, /* [5] protocol */
1117 { kSecAuthenticationTypeItemAttr
, 0, NULL
}, /* [6] authenticationType */
1118 { kSecCommentItemAttr
, 0, NULL
}, /* [7] comment */
1119 { kSecDescriptionItemAttr
, 0, NULL
}, /* [8] description */
1120 { kSecLabelItemAttr
, 0, NULL
}, /* [9] label */
1121 { kSecCreationDateItemAttr
, 0, NULL
}, /* [10] creation date */
1122 { kSecModDateItemAttr
, 0, NULL
}, /* [11] modification date */
1123 { kSecCreatorItemAttr
, 0, NULL
}, /* [12] creator */
1124 { kSecTypeItemAttr
, 0, NULL
}, /* [13] type */
1125 { kSecInvisibleItemAttr
, 0, NULL
}, /* [14] invisible */
1126 { kSecNegativeItemAttr
, 0, NULL
}, /* [15] negative */
1128 SecKeychainAttributeList attrList
= { sizeof(attr
) / sizeof(SecKeychainAttribute
), attr
};
1131 CFTypeRef keys
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1132 CFTypeRef values
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1136 // copy the item's attributes
1137 status
= SecKeychainItemCopyContent(item
, NULL
, &attrList
, NULL
, NULL
);
1138 require_noerr(status
, SecKeychainItemCopyContent_failed
);
1143 keys
[numValues
] = kSecClass
;
1144 values
[numValues
] = kSecClassInternetPassword
;
1147 // add kSecAttrServer
1148 if ( attrList
.attr
[0].length
> 0 ) {
1149 keys
[numValues
] = kSecAttrServer
;
1150 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[0].data
, attrList
.attr
[0].length
, kCFStringEncodingUTF8
, FALSE
);
1151 if ( values
[numValues
] != NULL
) {
1156 // add kSecAttrSecurityDomain
1157 if ( attrList
.attr
[1].length
> 0 ) {
1158 keys
[numValues
] = kSecAttrSecurityDomain
;
1159 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[1].data
, attrList
.attr
[1].length
, kCFStringEncodingUTF8
, FALSE
);
1160 if ( values
[numValues
] != NULL
) {
1165 // add kSecAttrAccount
1166 if ( attrList
.attr
[2].length
> 0 ) {
1167 keys
[numValues
] = kSecAttrAccount
;
1168 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[2].data
, attrList
.attr
[2].length
, kCFStringEncodingUTF8
, FALSE
);
1169 if ( values
[numValues
] != NULL
) {
1175 if ( attrList
.attr
[3].length
> 0 ) {
1176 keys
[numValues
] = kSecAttrPath
;
1177 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[3].data
, attrList
.attr
[3].length
, kCFStringEncodingUTF8
, FALSE
);
1178 if ( values
[numValues
] != NULL
) {
1184 if ( attrList
.attr
[4].length
> 0 ) {
1185 keys
[numValues
] = kSecAttrPort
;
1186 values
[numValues
] = CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[4].data
);
1187 if ( values
[numValues
] != NULL
) {
1192 // add kSecAttrProtocol
1193 if ( attrList
.attr
[5].length
> 0 ) {
1194 keys
[numValues
] = kSecAttrProtocol
;
1195 values
[numValues
] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType
*)attrList
.attr
[5].data
);
1196 if ( values
[numValues
] != NULL
) {
1197 CFRetain(values
[numValues
]);
1202 // add kSecAttrAuthenticationType
1203 if ( attrList
.attr
[6].length
> 0 ) {
1204 keys
[numValues
] = kSecAttrAuthenticationType
;
1205 values
[numValues
] = _SecAttrAuthenticationTypeForSecAuthenticationType( (SecAuthenticationType
) (*(SecProtocolType
*)attrList
.attr
[6].data
));
1206 if ( values
[numValues
] != NULL
) {
1207 CFRetain(values
[numValues
]);
1212 // add kSecAttrComment
1213 if ( attrList
.attr
[7].length
> 0 ) {
1214 keys
[numValues
] = kSecAttrComment
;
1215 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[7].data
, attrList
.attr
[7].length
, kCFStringEncodingUTF8
, FALSE
);
1216 if ( values
[numValues
] != NULL
) {
1221 // add kSecAttrDescription
1222 if ( attrList
.attr
[8].length
> 0 ) {
1223 keys
[numValues
] = kSecAttrDescription
;
1224 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[8].data
, attrList
.attr
[8].length
, kCFStringEncodingUTF8
, FALSE
);
1225 if ( values
[numValues
] != NULL
) {
1230 // add kSecAttrLabel
1231 if ( attrList
.attr
[9].length
> 0 ) {
1232 keys
[numValues
] = kSecAttrLabel
;
1233 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[9].data
, attrList
.attr
[9].length
, kCFStringEncodingUTF8
, FALSE
);
1234 if ( values
[numValues
] != NULL
) {
1239 // add kSecAttrCreationDate
1240 if ( attrList
.attr
[10].length
> 0 ) {
1241 CFDateRef creationDate
= NULL
;
1242 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[10].data
, attrList
.attr
[10].length
, &creationDate
);
1243 keys
[numValues
] = kSecAttrCreationDate
;
1244 values
[numValues
] = creationDate
;
1245 if ( values
[numValues
] != NULL
) {
1250 // add kSecAttrModificationDate
1251 if ( attrList
.attr
[11].length
> 0 ) {
1252 CFDateRef modDate
= NULL
;
1253 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[11].data
, attrList
.attr
[11].length
, &modDate
);
1254 keys
[numValues
] = kSecAttrModificationDate
;
1255 values
[numValues
] = modDate
;
1256 if ( values
[numValues
] != NULL
) {
1261 // add kSecCreatorItemAttr
1262 if ( attrList
.attr
[12].length
> 0 ) {
1263 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[12].data
);
1264 keys
[numValues
] = kSecAttrCreator
;
1265 values
[numValues
] = numberRef
;
1266 if ( values
[numValues
] != NULL
) {
1267 CFRetain(values
[numValues
]);
1272 // add kSecTypeItemAttr
1273 if ( attrList
.attr
[13].length
> 0 ) {
1274 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[13].data
);
1275 keys
[numValues
] = kSecAttrType
;
1276 values
[numValues
] = numberRef
;
1277 if ( values
[numValues
] != NULL
) {
1278 CFRetain(values
[numValues
]);
1283 // add kSecInvisibleItemAttr
1284 if ( attrList
.attr
[14].length
> 0 ) {
1285 uint32_t value
= *((uint32_t*)attrList
.attr
[14].data
);
1286 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1287 keys
[numValues
] = kSecAttrIsInvisible
;
1288 values
[numValues
] = boolRef
;
1289 if ( values
[numValues
] != NULL
) {
1290 CFRetain(values
[numValues
]);
1295 // add kSecNegativeItemAttr
1296 if ( attrList
.attr
[15].length
> 0 ) {
1297 uint32_t value
= *((uint32_t*)attrList
.attr
[15].data
);
1298 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1299 keys
[numValues
] = kSecAttrIsNegative
;
1300 values
[numValues
] = boolRef
;
1301 if ( values
[numValues
] != NULL
) {
1302 CFRetain(values
[numValues
]);
1307 // create the dictionary
1308 *dictionary
= CFDictionaryCreate(allocator
, keys
, values
, numValues
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1310 // release the values added to the dictionary
1311 for ( index
= 0; index
< numValues
; ++index
)
1313 CFRelease(values
[index
]);
1316 // and free the attributes
1317 (void) SecKeychainItemFreeContent(&attrList
, NULL
);
1319 SecKeychainItemCopyContent_failed
:
1326 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
1327 * attributes of the specified item class and item.
1330 _CreateAttributesDictionaryFromItem(
1331 CFAllocatorRef allocator
,
1332 SecItemClass itemClass
,
1333 SecKeychainItemRef item
,
1334 CFDictionaryRef
*dictionary
)
1338 case kSecInternetPasswordItemClass
:
1339 return _CreateAttributesDictionaryFromInternetPasswordItem(allocator
, item
, dictionary
);
1341 case kSecGenericPasswordItemClass
:
1342 return _CreateAttributesDictionaryFromGenericPasswordItem(allocator
, item
, dictionary
);
1344 case kSecCertificateItemClass
:
1345 return _CreateAttributesDictionaryFromCertificateItem(allocator
, item
, dictionary
);
1347 case kSecPublicKeyItemClass
:
1348 case kSecPrivateKeyItemClass
:
1349 case kSecSymmetricKeyItemClass
:
1350 return _CreateAttributesDictionaryFromKeyItem(allocator
, item
, dictionary
);
1361 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
1362 * by the _CreateSecKeychainAttributeListFromDictionary function.
1366 SecKeychainAttributeList
*attrListPtr
)
1370 if ( attrListPtr
!= NULL
) {
1371 if ( attrListPtr
->attr
!= NULL
) {
1372 // free any attribute data
1373 for ( index
= 0; index
< attrListPtr
->count
; ++index
) {
1374 free(attrListPtr
->attr
[index
].data
);
1376 // free the attribute array
1377 free(attrListPtr
->attr
);
1379 // free the attribute list
1385 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
1386 * attr using the data and tag parameters.
1388 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1389 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1392 _CFDataCreateAttribute(
1394 SecKeychainAttrType tag
,
1395 SecKeychainAttributePtr attr
)
1397 OSStatus status
= errSecSuccess
;
1400 // set the attribute tag
1403 // determine the attribute length
1404 attr
->length
= (UInt32
) CFDataGetLength(data
);
1405 range
= CFRangeMake(0, (CFIndex
)attr
->length
);
1407 // allocate memory for the attribute bytes
1408 attr
->data
= malloc(attr
->length
);
1409 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1411 // get the attribute bytes
1412 CFDataGetBytes(data
, range
, (UInt8
*)attr
->data
);
1420 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
1421 * attr using the string and tag parameters.
1423 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1424 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1427 _CFStringCreateAttribute(
1429 SecKeychainAttrType tag
,
1430 SecKeychainAttributePtr attr
)
1432 OSStatus status
= errSecSuccess
;
1435 // set the attribute tag
1438 // determine the attribute length
1439 range
= CFRangeMake(0, CFStringGetLength(string
));
1440 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, (CFIndex
*)&attr
->length
);
1442 // allocate memory for the attribute bytes
1443 attr
->data
= malloc(attr
->length
);
1444 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1446 // get the attribute bytes
1447 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*)attr
->data
, attr
->length
, NULL
);
1456 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1457 * from the attribute key/values in attrDictionary.
1459 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1460 * must be freed by the caller with _FreeAttrList()
1463 _CreateSecKeychainGenericPasswordAttributeListFromDictionary(
1464 CFDictionaryRef attrDictionary
,
1465 SecKeychainAttributeList
**attrList
)
1467 return _ConvertNewFormatToOldFormat(NULL
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, attrDictionary
, *attrList
);
1472 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
1473 * from the attribute key/values in attrDictionary.
1475 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1476 * must be freed by the caller with _FreeAttrList()
1479 _CreateSecKeychainCertificateAttributeListFromDictionary(
1480 CFDictionaryRef attrDictionary
,
1481 SecKeychainAttributeList
**attrList
)
1483 return _ConvertNewFormatToOldFormat(NULL
, gCertificateAttributes
, kNumberOfCertificateAttributes
, attrDictionary
, *attrList
);
1488 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
1489 * from the attribute key/values in attrDictionary.
1491 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1492 * must be freed by the caller with _FreeAttrList()
1495 _CreateSecKeychainKeyAttributeListFromDictionary(
1496 CFDictionaryRef attrDictionary
,
1497 SecKeychainAttributeList
**attrList
)
1500 //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
1501 return _ConvertNewFormatToOldFormat(NULL
, gKeyAttributes
, kNumberOfKeyAttributes
, attrDictionary
, *attrList
);
1503 // explicitly build attribute list for supported key attributes
1504 // NOTE: this code supports only MaxSecKeyAttributes (15) attributes
1505 const int MaxSecKeyAttributes
= 15;
1509 SecKeychainAttributeList
*attrListPtr
;
1511 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1512 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1514 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeyAttributes
, sizeof(SecKeychainAttribute
));
1515 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1517 // [0] get the kSecKeyKeyClass value
1518 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyClass
, (const void **)&value
) && value
) {
1519 UInt32 keyRecordValue
= 0;
1520 if (CFEqual(kSecAttrKeyClassPublic
, value
))
1521 keyRecordValue
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
1522 else if (CFEqual(kSecAttrKeyClassPrivate
, value
))
1523 keyRecordValue
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
1524 else if (CFEqual(kSecAttrKeyClassSymmetric
, value
))
1525 keyRecordValue
= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
;
1527 // only use this attribute if we recognize the value!
1528 if (keyRecordValue
!= 0) {
1529 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1530 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1532 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyClass
;
1533 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1534 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyRecordValue
;
1536 ++attrListPtr
->count
;
1540 // [1] get the kSecKeyPrintName string
1541 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) && value
) {
1542 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyPrintName
, &attrListPtr
->attr
[attrListPtr
->count
]);
1543 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1545 ++attrListPtr
->count
;
1548 // [2] get the kSecKeyPermanent boolean
1549 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsPermanent
, (const void **)&value
) && value
) {
1550 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1551 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1553 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyPermanent
;
1554 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1555 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1557 ++attrListPtr
->count
;
1560 // [3] get the kSecKeyLabel string
1561 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationLabel
, (const void **)&value
) && value
) {
1562 if (CFStringGetTypeID() == CFGetTypeID(value
))
1563 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1564 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1565 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1567 status
= errSecParam
;
1569 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1571 ++attrListPtr
->count
;
1574 // [4] get the kSecKeyApplicationTag data
1575 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationTag
, (const void **)&value
) && value
) {
1576 if (CFStringGetTypeID() == CFGetTypeID(value
))
1577 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1578 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1579 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1581 status
= errSecParam
;
1583 require_noerr_quiet(status
, CFDataCreateAttribute_failed
);
1584 ++attrListPtr
->count
;
1587 // [5] get the kSecKeyKeyType number
1588 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyType
, (const void **)&value
) && value
) {
1589 UInt32 keyAlgValue
= _SecAlgorithmTypeFromSecAttrKeyType(value
);
1590 if (keyAlgValue
!= 0) {
1591 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1592 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1594 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyType
;
1595 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1596 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyAlgValue
;
1598 ++attrListPtr
->count
;
1602 // [6] get the kSecKeyKeySizeInBits number
1603 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeySizeInBits
, (const void **)&value
) && value
) {
1604 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1605 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1606 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1608 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeySizeInBits
;
1609 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1610 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1612 ++attrListPtr
->count
;
1616 // [7] get the kSecKeyEffectiveKeySize number
1617 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrEffectiveKeySize
, (const void **)&value
) && value
) {
1618 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1619 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1620 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1622 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEffectiveKeySize
;
1623 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1624 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1626 ++attrListPtr
->count
;
1630 // [8] get the kSecKeyEncrypt boolean
1631 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanEncrypt
, (const void **)&value
) && value
) {
1632 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1633 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1634 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1636 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEncrypt
;
1637 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1638 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1640 ++attrListPtr
->count
;
1644 // [9] get the kSecKeyDecrypt boolean
1645 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDecrypt
, (const void **)&value
) && value
) {
1646 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1647 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1648 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1650 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDecrypt
;
1651 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1652 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1654 ++attrListPtr
->count
;
1658 // [10] get the kSecKeyDerive boolean
1659 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDerive
, (const void **)&value
) && value
) {
1660 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1661 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1662 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1664 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDerive
;
1665 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1666 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1668 ++attrListPtr
->count
;
1672 // [11] get the kSecKeySign boolean
1673 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanSign
, (const void **)&value
) && value
) {
1674 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1675 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1676 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1678 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeySign
;
1679 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1680 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1682 ++attrListPtr
->count
;
1686 // [12] get the kSecKeyVerify boolean
1687 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanVerify
, (const void **)&value
) && value
) {
1688 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1689 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1690 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1692 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyVerify
;
1693 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1694 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1696 ++attrListPtr
->count
;
1700 // [13] get the kSecKeyWrap boolean
1701 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanWrap
, (const void **)&value
) && value
) {
1702 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1703 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1704 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1706 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyWrap
;
1707 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1708 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1710 ++attrListPtr
->count
;
1714 // [14] get the kSecKeyUnwrap boolean
1715 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanUnwrap
, (const void **)&value
) && value
) {
1716 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1717 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1718 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1720 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyUnwrap
;
1721 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1722 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1724 ++attrListPtr
->count
;
1728 // return the pointer to the attrList
1729 *attrList
= attrListPtr
;
1731 return ( errSecSuccess
);
1735 malloc_number_failed
:
1736 CFDataCreateAttribute_failed
:
1737 CFStringCreateAttribute_failed
:
1738 malloc_attrPtr_failed
:
1740 // free any attributes
1741 _FreeAttrList(attrListPtr
);
1743 calloc_attrListPtr_failed
:
1745 return ( errSecBufferTooSmall
);
1750 static CFTypeRef
copyNumber(CFTypeRef obj
)
1755 CFTypeID tid
= CFGetTypeID(obj
);
1756 if (tid
== CFNumberGetTypeID())
1762 if (tid
== CFBooleanGetTypeID())
1764 SInt32 value
= CFBooleanGetValue((CFBooleanRef
)obj
);
1765 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1768 if (tid
== CFStringGetTypeID())
1770 SInt32 value
= CFStringGetIntValue((CFStringRef
)obj
);
1771 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
1772 /* If a string converted to an int isn't equal to the int printed as
1773 a string, return a NULL instead. */
1774 if (!CFEqual(t
, obj
))
1780 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1786 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1787 * from the attribute key/values in attrDictionary.
1789 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1790 * must be freed by the caller with _FreeAttrList()
1793 _CreateSecKeychainInternetPasswordAttributeListFromDictionary(
1794 CFDictionaryRef attrDictionary
,
1795 SecKeychainAttributeList
**attrList
)
1797 // explicitly build attribute list for supported key attributes
1798 // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
1799 const int MaxSecKeychainAttributes
= 14;
1803 SecKeychainAttributeList
*attrListPtr
;
1805 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1806 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1808 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeychainAttributes
, sizeof(SecKeychainAttribute
));
1809 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1812 // [0] get the serverName string
1813 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrServer
, (const void **)&value
) ) {
1814 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecServerItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1815 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1817 ++attrListPtr
->count
;
1820 // [1] get the securityDomain string
1821 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrSecurityDomain
, (const void **)&value
) ) {
1822 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecSecurityDomainItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1823 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1825 ++attrListPtr
->count
;
1828 // [2] get the accountName string
1829 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAccount
, (const void **)&value
) ) {
1830 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecAccountItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1831 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1833 ++attrListPtr
->count
;
1836 // [3] get the path string
1837 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPath
, (const void **)&value
) ) {
1838 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecPathItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1839 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1841 ++attrListPtr
->count
;
1844 // [4] get the port number
1845 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPort
, (const void **)&value
) ) {
1846 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt16
));
1847 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1849 CFTypeRef num
= copyNumber(value
);
1850 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1851 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecPortItemAttr
;
1852 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt16
);
1853 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt16Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1856 ++attrListPtr
->count
;
1859 // [5] get the protocol
1860 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrProtocol
, (const void **)&value
) ) {
1861 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecProtocolType
));
1862 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_protocol_failed
, status
= errSecBufferTooSmall
);
1864 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecProtocolItemAttr
;
1865 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecProtocolType
);
1866 *(SecProtocolType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecProtocolTypeForSecAttrProtocol(value
);
1868 ++attrListPtr
->count
;
1871 // [6] get the authenticationType
1872 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAuthenticationType
, (const void **)&value
) ) {
1873 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecAuthenticationType
));
1874 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_authenticationType_failed
, status
= errSecBufferTooSmall
);
1876 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecAuthenticationTypeItemAttr
;
1877 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecAuthenticationType
);
1878 *(SecAuthenticationType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecAuthenticationTypeForSecAttrAuthenticationType(value
);
1880 ++attrListPtr
->count
;
1883 // [7] get the comment string
1884 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrComment
, (const void **)&value
) ) {
1885 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecCommentItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1886 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1888 ++attrListPtr
->count
;
1891 // [8] get the description string
1892 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrDescription
, (const void **)&value
) ) {
1893 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecDescriptionItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1894 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1896 ++attrListPtr
->count
;
1899 // [9] get the label string
1900 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) ) {
1901 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecLabelItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1902 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1904 ++attrListPtr
->count
;
1907 // [10] get the creator code
1908 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCreator
, (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
= kSecCreatorItemAttr
;
1915 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1916 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1919 ++attrListPtr
->count
;
1922 // [11] get the type code
1923 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrType
, (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 CFTypeRef num
= copyNumber(value
);
1928 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1929 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecTypeItemAttr
;
1930 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1931 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1934 ++attrListPtr
->count
;
1937 // [12] get the invisible flag
1938 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsInvisible
, (const void **)&value
) ) {
1939 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1940 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1942 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecInvisibleItemAttr
;
1943 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1944 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1946 ++attrListPtr
->count
;
1949 // [13] get the negative flag
1950 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsNegative
, (const void **)&value
) ) {
1951 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1952 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1954 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecNegativeItemAttr
;
1955 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1956 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1958 ++attrListPtr
->count
;
1961 // return the pointer to the attrList
1962 *attrList
= attrListPtr
;
1964 return ( errSecSuccess
);
1968 malloc_authenticationType_failed
:
1969 malloc_protocol_failed
:
1971 CFStringCreateAttribute_failed
:
1972 malloc_attrPtr_failed
:
1974 // free any attributes
1975 _FreeAttrList(attrListPtr
);
1977 calloc_attrListPtr_failed
:
1979 return ( errSecBufferTooSmall
);
1984 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
1985 * from the attribute key/values in attrDictionary for the specified item class.
1987 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1988 * must be freed by the caller with _FreeAttrList()
1991 _CreateSecKeychainAttributeListFromDictionary(
1992 CFDictionaryRef attrDictionary
,
1993 SecItemClass itemClass
,
1994 SecKeychainAttributeList
**attrList
)
1998 case kSecInternetPasswordItemClass
:
1999 return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
2001 case kSecGenericPasswordItemClass
:
2002 return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
2004 case kSecCertificateItemClass
:
2005 return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary
, attrList
);
2007 case kSecPublicKeyItemClass
:
2008 case kSecPrivateKeyItemClass
:
2009 case kSecSymmetricKeyItemClass
:
2010 return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary
, attrList
);
2020 * _AppNameFromSecTrustedApplication attempts to pull the name of the
2021 * application/tool from the SecTrustedApplicationRef.
2023 static CFStringRef CF_RETURNS_RETAINED
2024 _AppNameFromSecTrustedApplication(
2025 CFAllocatorRef alloc
,
2026 SecTrustedApplicationRef appRef
)
2030 CFDataRef appDataRef
;
2034 // get the data for item's application/tool
2035 status
= SecTrustedApplicationCopyData(appRef
, &appDataRef
);
2036 if ( status
== errSecSuccess
) {
2039 // convert it to a CFString potentially containing the path
2040 path
= CFStringCreateWithCString(NULL
, (char *)CFDataGetBytePtrVoid(appDataRef
), kCFStringEncodingUTF8
);
2041 if ( path
!= NULL
) {
2042 // the path has to start with a "/" and cannot contain "://"
2043 if ( CFStringHasPrefix(path
, CFSTR("/")) && (CFStringFind(path
, CFSTR("://"), 0).location
== kCFNotFound
) ) {
2044 CFRange nameRange
, compRg
;
2046 nameRange
= CFRangeMake(0, CFStringGetLength(path
));
2048 // remove the trailing slashes (if any)
2049 while ( (nameRange
.length
> 0) && (CFStringGetCharacterAtIndex(path
, nameRange
.length
- 1) == '/') ) {
2050 nameRange
.length
--;
2053 if ( nameRange
.length
> 0 ) {
2054 // find last slash and adjust nameRange to be everything after it
2055 if ( CFStringFindWithOptions(path
, CFSTR("/"), nameRange
, kCFCompareBackwards
, &compRg
) ) {
2056 nameRange
.length
= nameRange
.location
+ nameRange
.length
- (compRg
.location
+ 1);
2057 nameRange
.location
= compRg
.location
+ 1;
2060 result
= CFStringCreateWithSubstring(alloc
, path
, nameRange
);
2065 CFRelease(appDataRef
);
2071 /* (This function really belongs in SecIdentity.cpp!)
2073 * Returns the public key item corresponding to the identity, if it exists in
2074 * the same keychain as the private key. Note that the public key might not
2075 * exist in the same keychain (e.g. if the identity was imported via PKCS12),
2076 * in which case it will not be found.
2079 _SecIdentityCopyPublicKey(
2080 SecIdentityRef identityRef
,
2081 SecKeyRef
*publicKeyRef
)
2085 SecKeychainAttribute attr
= { kSecKeyLabel
, 0, NULL
};
2086 SecKeychainAttributeList attrList
= { 1, &attr
};
2087 SecKeychainAttributeList
*keyAttrList
= NULL
;
2088 SecKeychainAttributeInfo
*info
= NULL
;
2089 SecKeychainSearchRef search
= NULL
;
2090 SecKeychainRef keychain
= NULL
;
2091 SecKeychainItemRef privateKey
= NULL
;
2092 SecKeychainItemRef publicKey
= NULL
;
2094 status
= SecIdentityCopyPrivateKey(identityRef
, (SecKeyRef
*)&privateKey
);
2096 goto error_exit
; // identity must have a private key
2098 status
= SecKeychainItemCopyKeychain(privateKey
, &keychain
);
2100 goto error_exit
; // private key must have a keychain, so we can get the attribute info for it
2102 status
= SecKeychainAttributeInfoForItemID(keychain
, kSecPrivateKeyItemClass
, &info
);
2104 goto error_exit
; // unable to get the attribute info (i.e. database schema) for private keys
2106 status
= SecKeychainItemCopyAttributesAndData(privateKey
, info
, NULL
, &keyAttrList
, NULL
, NULL
);
2108 goto error_exit
; // unable to get the key label attribute for the private key
2111 // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
2112 for (count
= 0; count
< keyAttrList
->count
; count
++) {
2113 if (keyAttrList
->attr
[count
].tag
== kSecKeyLabel
) {
2114 attr
.length
= keyAttrList
->attr
[count
].length
;
2115 attr
.data
= keyAttrList
->attr
[count
].data
;
2119 if (!attr
.length
|| !attr
.data
) {
2120 status
= errSecNoSuchAttr
;
2121 goto error_exit
; // the private key didn't have the hash of the public key in its kSecKeyLabel
2123 status
= SecKeychainSearchCreateFromAttributes(keychain
, kSecPublicKeyItemClass
, &attrList
, &search
);
2125 goto error_exit
; // unable to create the search reference
2127 status
= SecKeychainSearchCopyNext(search
, &publicKey
);
2129 goto error_exit
; // unable to find the public key
2133 *publicKeyRef
= (SecKeyRef
)publicKey
;
2135 CFRelease(publicKey
);
2138 if (status
!= errSecSuccess
) {
2140 *publicKeyRef
= NULL
;
2142 CFRelease(publicKey
);
2148 SecKeychainItemFreeAttributesAndData(keyAttrList
, NULL
);
2151 SecKeychainFreeAttributeInfo(info
);
2154 CFRelease(keychain
);
2157 CFRelease(privateKey
);
2164 * Deletes a keychain item if the current application/tool is the only application/tool
2165 * with decrypt access to that keychain item. If more than one application/tool
2166 * has decrypt access to the keychain item, the item is left on the keychain.
2168 * TBD: If more than one app/tool has access to the keychain item, we should remove
2169 * the current app/tool's decrypt access. There's no easy way to do that with
2170 * current keychain APIs without bringing up the security UI.
2173 _SafeSecKeychainItemDelete(
2174 SecKeychainItemRef itemRef
)
2177 SecAccessRef access
= NULL
;
2178 CFArrayRef aclList
= NULL
;
2179 SecACLRef acl
= NULL
;
2180 CFArrayRef appList
= NULL
;
2181 CFStringRef description
= NULL
;
2182 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
;
2183 CFIndex idx
, count
= 0;
2184 SecTrustedApplicationRef currentAppRef
= NULL
;
2185 CFStringRef itemAppName
= NULL
, currentAppName
= NULL
;
2187 SecItemClass itemClass
= (SecItemClass
)0;
2188 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2189 if (!(itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2190 // only perform the access control safety check on deletion of password credentials;
2191 // if the item is of some other type, delete it normally.
2192 return SecKeychainItemDelete(itemRef
);
2195 // skip access control checking for web form passwords: <rdar://10957301>
2196 // This permits Safari to manage the removal of all web form passwords,
2197 // regardless of whether they are shared by multiple applications.
2198 if (itemClass
== kSecInternetPasswordItemClass
) {
2199 UInt32 tags
[1] = { kSecAuthenticationTypeItemAttr
};
2200 SecKeychainAttributeInfo attrInfo
= { 1, tags
, NULL
};
2201 SecKeychainAttributeList
*attrs
= NULL
;
2202 status
= SecKeychainItemCopyAttributesAndData(itemRef
, &attrInfo
, NULL
, &attrs
, NULL
, NULL
);
2203 if (!status
&& attrs
) {
2204 bool webFormPassword
= (attrs
->attr
[0].length
== 4 && (!memcmp(attrs
->attr
[0].data
, "form", 4)));
2205 SecKeychainItemFreeAttributesAndData(attrs
, NULL
);
2206 if (webFormPassword
) {
2207 return SecKeychainItemDelete(itemRef
);
2212 // copy the access of the keychain item
2213 status
= SecKeychainItemCopyAccess(itemRef
, &access
);
2214 require_noerr(status
, finish
);
2215 require_quiet(access
!= NULL
, finish
);
2217 // copy the decrypt access control lists -- this is what has access to the keychain item
2218 status
= SecAccessCopySelectedACLList(access
, CSSM_ACL_AUTHORIZATION_DECRYPT
, &aclList
);
2219 require_noerr(status
, finish
);
2220 require_quiet(aclList
!= NULL
, finish
);
2222 // get the access control list
2223 acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, 0);
2224 require_quiet(acl
!= NULL
, finish
);
2226 // copy the application list, description, and CSSM prompt selector for a given access control list entry
2227 status
= SecACLCopySimpleContents(acl
, &appList
, &description
, &promptSelector
);
2228 require_noerr(status
, finish
);
2229 require_quiet(appList
!= NULL
, finish
);
2231 // does the calling application/tool have decrypt access to this item?
2232 count
= CFArrayGetCount(appList
);
2233 for ( idx
= 0; idx
< count
; idx
++ ) {
2234 // get SecTrustedApplicationRef for this entry
2235 SecTrustedApplicationRef itemAppRef
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(appList
, idx
);
2236 require_quiet(itemAppRef
!= NULL
, finish
);
2238 // copy the name out
2239 CFReleaseSafe(itemAppName
);
2240 itemAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), itemAppRef
);
2241 if (itemAppName
== NULL
) {
2243 * If there is no app name, it's probably because it's not an appname
2244 * in the ACE but an entitlement/info.plist based rule instead;
2245 * just let the caller have it. */
2250 // create SecTrustedApplicationRef for current application/tool
2251 CFReleaseNull(currentAppRef
);
2252 status
= SecTrustedApplicationCreateFromPath(NULL
, ¤tAppRef
);
2253 require_noerr(status
, finish
);
2254 require_quiet(currentAppRef
!= NULL
, finish
);
2256 // copy the name out
2257 CFReleaseSafe(currentAppName
);
2258 currentAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), currentAppRef
);
2259 require_quiet(currentAppName
!= NULL
, finish
);
2261 // compare the names to see if we own the decrypt access
2262 // TBD: validation of membership in an application group
2263 if ( CFStringCompare(currentAppName
, itemAppName
, 0) == kCFCompareEqualTo
) {
2271 CFReleaseSafe(currentAppName
);
2272 CFReleaseSafe(itemAppName
);
2273 CFReleaseSafe(currentAppRef
);
2274 CFReleaseSafe(description
);
2275 CFReleaseSafe(appList
);
2276 CFReleaseSafe(aclList
);
2277 CFReleaseSafe(access
);
2279 if ((count
== 0) || (status
== errSecVerifyFailed
)) {
2280 // no "owners" remain in the ACL list (or unable to get ACL)
2281 status
= SecKeychainItemDelete(itemRef
);
2283 // caller is not the "owner" of the item
2284 status
= errSecInvalidOwnerEdit
;
2291 _ReplaceKeychainItem(
2292 SecKeychainItemRef itemToUpdate
,
2293 SecKeychainAttributeList
*changeAttrList
,
2298 SecItemClass itemClass
;
2299 SecKeychainAttributeInfo
*info
= NULL
;
2300 SecKeychainAttributeList
*attrList
= NULL
;
2301 SecKeychainAttributeList newAttrList
= { 0, NULL
};
2302 SecKeychainRef keychain
= NULL
;
2303 SecKeychainItemRef newItem
= NULL
;
2305 int priority
= LOG_DEBUG
;
2306 const char *format
= "ReplaceKeychainItem (%d) error %d";
2308 // get existing item's keychain
2309 status
= SecKeychainItemCopyKeychain(itemToUpdate
, &keychain
);
2310 if (status
) { secitemlog(priority
, format
, 1, (int)status
); }
2311 require_noerr(status
, replace_failed
);
2313 // get attribute info (i.e. database schema) for the item class
2314 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2315 if (status
) { secitemlog(priority
, format
, 2, (int)status
); }
2316 require_noerr(status
, replace_failed
);
2320 case kSecInternetPasswordItemClass
:
2321 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
2323 case kSecGenericPasswordItemClass
:
2324 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
2330 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
2331 if (status
) { secitemlog(priority
, format
, 3, (int)status
); }
2333 // get item's existing attributes (but not data!)
2334 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, info
, &itemClass
, &attrList
, NULL
, NULL
);
2335 if (status
) { secitemlog(priority
, format
, 4, (int)status
); }
2336 require(attrList
!= NULL
, replace_failed
);
2338 // move aside the item by changing a primary attribute
2339 // (currently only for passwords)
2340 if (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
) {
2341 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
2342 CFStringRef uuidStr
= (uuid
) ? CFUUIDCreateString(kCFAllocatorDefault
, uuid
) : CFSTR("MOVED");
2343 CFReleaseSafe(uuid
);
2345 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(uuidStr
), kCFStringEncodingUTF8
) + 1;
2346 char* buffer
= (char*) malloc(maxLength
);
2348 if (CFStringGetCString(uuidStr
, buffer
, maxLength
, kCFStringEncodingUTF8
)) {
2349 UInt32 length
= (UInt32
)strlen(buffer
);
2350 SecKeychainAttribute attrs
[] = { { kSecAccountItemAttr
, length
, (char*)buffer
}, };
2351 SecKeychainAttributeList updateAttrList
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
2352 status
= SecKeychainItemModifyAttributesAndData(itemToUpdate
, &updateAttrList
, 0, NULL
);
2353 if (status
) { secitemlog(priority
, format
, 5, (int)status
); }
2354 if (status
== errSecVerifyFailed
) {
2355 // still unable to change attrs? delete unconditionally here
2356 status
= SecKeychainItemDelete(itemToUpdate
);
2357 if (status
) { secitemlog(priority
, format
, 6, (int)status
); }
2362 CFReleaseSafe(uuidStr
);
2365 require_noerr(status
, replace_failed
);
2367 // make attribute list for new item (the data is still owned by attrList)
2368 newAttrList
.count
= attrList
->count
;
2369 newAttrList
.attr
= (SecKeychainAttribute
*) calloc(attrList
->count
, sizeof(SecKeychainAttribute
));
2371 for (i
=0, newCount
=0; i
< attrList
->count
; i
++) {
2372 if (attrList
->attr
[i
].length
> 0) {
2373 newAttrList
.attr
[newCount
++] = attrList
->attr
[i
];
2376 newAttrList
.count
= newCount
;
2378 // create new item in the same keychain
2379 status
= SecKeychainItemCreateFromContent(itemClass
, &newAttrList
,
2380 (UInt32
)((itemData
) ? CFDataGetLength(itemData
) : 0),
2381 (const void *)((itemData
) ? CFDataGetBytePtr(itemData
) : NULL
),
2382 keychain
, NULL
, &newItem
);
2383 if (status
) { secitemlog(priority
, format
, 7, (int)status
); }
2384 require_noerr(status
, replace_failed
);
2386 // delete the old item unconditionally once new item exists
2387 status
= SecKeychainItemDelete(itemToUpdate
);
2389 // update the new item with changed attributes, if any
2390 status
= (changeAttrList
) ? SecKeychainItemModifyContent(newItem
, changeAttrList
, 0, NULL
) : errSecSuccess
;
2391 if (status
) { secitemlog(priority
, format
, 8, (int)status
); }
2392 if (status
== errSecSuccess
) {
2393 // say the item already exists, because it does now. <rdar://19063674>
2394 status
= errSecDuplicateItem
;
2398 if (newAttrList
.attr
) {
2399 free(newAttrList
.attr
);
2402 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
2405 SecKeychainFreeAttributeInfo(info
);
2407 CFReleaseSafe(newItem
);
2408 CFReleaseSafe(keychain
);
2414 _UpdateKeychainItem(CFTypeRef item
, CFDictionaryRef changedAttributes
)
2416 // This function updates a single keychain item, which may be specified as
2417 // a reference, persistent reference or attribute dictionary, with the
2418 // attributes provided.
2420 OSStatus status
= errSecSuccess
;
2425 SecItemClass itemClass
= (SecItemClass
) 0;
2426 SecAccessRef access
= NULL
;
2427 SecKeychainAttributeList
*changeAttrList
= NULL
;
2428 SecKeychainItemRef itemToUpdate
= NULL
;
2429 CFDataRef theData
= NULL
;
2430 CFTypeID itemType
= CFGetTypeID(item
);
2432 // validate input item (must be convertible to a SecKeychainItemRef)
2433 if (SecKeychainItemGetTypeID() == itemType
||
2434 SecCertificateGetTypeID() == itemType
||
2435 SecKeyGetTypeID() == itemType
) {
2436 // item is already a reference, retain it
2437 itemToUpdate
= (SecKeychainItemRef
) CFRetain(item
);
2439 else if (CFDataGetTypeID() == itemType
) {
2440 // item is a persistent reference, must convert it
2441 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToUpdate
);
2443 else if (CFDictionaryGetTypeID() == itemType
) {
2444 // item is a dictionary
2445 CFTypeRef value
= NULL
;
2446 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2447 // kSecValueRef value is a SecKeychainItemRef, retain it
2448 itemToUpdate
= (SecKeychainItemRef
) CFRetain(value
);
2450 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2451 // kSecValuePersistentRef value is a persistent reference, must convert it
2452 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToUpdate
);
2455 else if (SecIdentityGetTypeID() == itemType
) {
2456 // item is a certificate + private key; since we can't really change the
2457 // certificate's attributes, assume we want to update the private key
2458 status
= SecIdentityCopyPrivateKey((SecIdentityRef
)item
, (SecKeyRef
*)&itemToUpdate
);
2460 require_action(itemToUpdate
!= NULL
, update_failed
, status
= errSecInvalidItemRef
);
2461 require_noerr(status
, update_failed
);
2463 status
= SecKeychainItemCopyContent(itemToUpdate
, &itemClass
, NULL
, NULL
, NULL
);
2464 require_noerr(status
, update_failed
);
2466 // build changeAttrList from changedAttributes dictionary
2469 case kSecInternetPasswordItemClass
:
2471 status
= _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2472 require_noerr(status
, update_failed
);
2476 case kSecGenericPasswordItemClass
:
2478 status
= _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2479 require_noerr(status
, update_failed
);
2483 case kSecCertificateItemClass
:
2485 status
= _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2486 require_noerr(status
, update_failed
);
2490 case kSecPublicKeyItemClass
:
2491 case kSecPrivateKeyItemClass
:
2492 case kSecSymmetricKeyItemClass
:
2494 status
= _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2495 require_noerr(status
, update_failed
);
2498 case kSecAppleSharePasswordItemClass
:
2500 // do nothing (legacy behavior).
2507 // (if the caller is not updating the password, this value will be NULL)
2508 theData
= (CFDataRef
)CFDictionaryGetValue(changedAttributes
, kSecValueData
);
2509 if (theData
!= NULL
) {
2510 require_action(CFDataGetTypeID() == CFGetTypeID(theData
), update_failed
, status
= errSecParam
);
2513 status
= SecKeychainItemModifyContent(itemToUpdate
,
2514 (!changeAttrList
|| changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2515 (theData
!= NULL
) ? (UInt32
)CFDataGetLength(theData
) : 0,
2516 (theData
!= NULL
) ? CFDataGetBytePtrVoid(theData
) : NULL
);
2517 require_noerr(status
, update_failed
);
2519 // one more thing... update access?
2520 if (CFDictionaryGetValueIfPresent(changedAttributes
, kSecAttrAccess
, (const void **)&access
)) {
2521 status
= SecKeychainItemSetAccess(itemToUpdate
, access
);
2525 if (status
== errSecVerifyFailed
&&
2526 (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2527 // if we got a cryptographic failure updating a password item, it needs to be replaced
2528 status
= _ReplaceKeychainItem(itemToUpdate
,
2529 (!changeAttrList
|| changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2533 CFRelease(itemToUpdate
);
2534 _FreeAttrList(changeAttrList
);
2539 _DeleteKeychainItem(CFTypeRef item
)
2541 // This function deletes a single keychain item, which may be specified as
2542 // a reference, persistent reference or attribute dictionary. It will not
2543 // delete non-keychain items or aggregate items (such as a SecIdentityRef);
2544 // it is assumed that the caller will pass identity components separately.
2546 OSStatus status
= errSecSuccess
;
2551 SecKeychainItemRef itemToDelete
= NULL
;
2552 CFTypeID itemType
= CFGetTypeID(item
);
2553 if (SecKeychainItemGetTypeID() == itemType
||
2554 SecCertificateGetTypeID() == itemType
||
2555 SecKeyGetTypeID() == itemType
) {
2556 // item is already a reference, retain it
2557 itemToDelete
= (SecKeychainItemRef
) CFRetain(item
);
2559 else if (CFDataGetTypeID() == itemType
) {
2560 // item is a persistent reference, must convert it
2561 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToDelete
);
2563 else if (CFDictionaryGetTypeID() == itemType
) {
2564 // item is a dictionary
2565 CFTypeRef value
= NULL
;
2566 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2567 // kSecValueRef value is a SecKeychainItemRef, retain it
2568 itemToDelete
= (SecKeychainItemRef
) CFRetain(value
);
2570 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2571 // kSecValuePersistentRef value is a persistent reference, must convert it
2572 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToDelete
);
2578 status
= _SafeSecKeychainItemDelete(itemToDelete
);
2580 CFRelease(itemToDelete
);
2587 _DeleteIdentity(SecIdentityRef identity
)
2589 OSStatus status
, result
= errSecSuccess
;
2590 SecKeyRef privateKey
= NULL
;
2591 SecCertificateRef certificate
= NULL
;
2593 status
= SecIdentityCopyPrivateKey(identity
, &privateKey
);
2595 SecKeyRef publicKey
= NULL
;
2596 status
= _SecIdentityCopyPublicKey(identity
, &publicKey
);
2598 status
= _DeleteKeychainItem(publicKey
);
2599 CFRelease(publicKey
);
2601 status
= _DeleteKeychainItem(privateKey
);
2604 if (privateKey
) CFRelease(privateKey
);
2605 if (status
) result
= status
;
2607 status
= SecIdentityCopyCertificate(identity
, &certificate
);
2609 status
= _DeleteKeychainItem(certificate
);
2612 if (certificate
) CFRelease(certificate
);
2613 if (status
) result
= status
;
2619 _UpdateAggregateStatus(OSStatus newStatus
, OSStatus curStatus
, OSStatus baseStatus
)
2621 // This function is used when atomically processing multiple items,
2622 // where an overall error result must be returned for the entire operation.
2623 // When newStatus is something other than errSecSuccess, we want to keep the "most
2624 // interesting" status (which usually will be newStatus, unless curStatus is
2625 // already set; in that case, newStatus can trump curStatus only by being
2626 // something different than baseStatus.)
2628 OSStatus result
= curStatus
;
2630 if (newStatus
!= errSecSuccess
) {
2632 if (curStatus
!= errSecSuccess
) {
2633 result
= (newStatus
!= baseStatus
) ? newStatus
: curStatus
;
2640 _AddDictValueToOtherDict(const void *key
, const void *value
, void *context
)
2642 // CFDictionaryApplierFunction
2643 // This function just takes the given key/value pair,
2644 // and adds it to another dictionary supplied in the context argument.
2646 CFMutableDictionaryRef dict
= *((CFMutableDictionaryRef
*) context
);
2648 CFDictionaryAddValue(dict
, key
, value
);
2652 static CFStringCompareFlags
2653 _StringCompareFlagsFromQuery(CFDictionaryRef query
)
2656 CFStringCompareFlags flags
= 0;
2657 if (!query
) return flags
;
2659 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&value
) ||
2660 CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2661 flags
|= kCFCompareAnchored
;
2663 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2664 flags
|= kCFCompareBackwards
;
2666 if (CFDictionaryGetValueIfPresent(query
, kSecMatchCaseInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2667 flags
|= kCFCompareCaseInsensitive
;
2669 if (CFDictionaryGetValueIfPresent(query
, kSecMatchDiacriticInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2670 flags
|= kCFCompareDiacriticInsensitive
;
2672 if (CFDictionaryGetValueIfPresent(query
, kSecMatchWidthInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2673 flags
|= kCFCompareWidthInsensitive
;
2679 _CssmKeyUsageFromQuery(CFDictionaryRef query
)
2682 uint32 keyUsage
= 0;
2683 if (!query
) return keyUsage
;
2685 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanEncrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2686 keyUsage
|= CSSM_KEYUSE_ENCRYPT
;
2688 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDecrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2689 keyUsage
|= CSSM_KEYUSE_DECRYPT
;
2691 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanSign
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2692 keyUsage
|= CSSM_KEYUSE_SIGN
;
2694 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanVerify
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2695 keyUsage
|= CSSM_KEYUSE_VERIFY
;
2697 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanWrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2698 keyUsage
|= CSSM_KEYUSE_WRAP
;
2700 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanUnwrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2701 keyUsage
|= CSSM_KEYUSE_UNWRAP
;
2703 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDerive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2704 keyUsage
|= CSSM_KEYUSE_DERIVE
;
2710 _ConvertItemClass(const void* item
, const void* keyClass
, Boolean
*isIdentity
)
2712 SecItemClass itemClass
= (SecItemClass
) 0;
2713 if (isIdentity
) *isIdentity
= false;
2715 if (CFEqual(item
, kSecClassGenericPassword
)) {
2716 itemClass
= kSecGenericPasswordItemClass
;
2718 else if (CFEqual(item
, kSecClassInternetPassword
)) {
2719 itemClass
= kSecInternetPasswordItemClass
;
2721 else if (CFEqual(item
, kSecClassCertificate
)) {
2722 itemClass
= kSecCertificateItemClass
;
2724 else if (CFEqual(item
, kSecClassIdentity
)) {
2725 // will perform a certificate lookup
2726 itemClass
= kSecCertificateItemClass
;
2727 if (isIdentity
) *isIdentity
= true;
2729 else if (CFEqual(item
, kSecClassKey
)) {
2730 // examine second parameter to determine type of key
2731 if (!keyClass
|| CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
2732 itemClass
= kSecSymmetricKeyItemClass
;
2734 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPublic
)) {
2735 itemClass
= kSecPublicKeyItemClass
;
2737 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
2738 itemClass
= kSecPrivateKeyItemClass
;
2746 _ItemClassFromItemList(CFArrayRef itemList
)
2748 // Given a list of items (standard or persistent references),
2749 // determine whether they all have the same item class. Returns
2750 // the item class, or 0 if multiple classes in list.
2751 SecItemClass result
= (SecItemClass
) 0;
2752 CFIndex index
, count
= (itemList
) ? CFArrayGetCount(itemList
) : 0;
2753 for (index
=0; index
< count
; index
++) {
2754 CFTypeRef item
= (CFTypeRef
) CFArrayGetValueAtIndex(itemList
, index
);
2756 SecKeychainItemRef itemRef
= NULL
;
2758 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
2759 // persistent reference, resolve first
2760 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemRef
);
2763 itemRef
= (SecKeychainItemRef
) CFRetain(item
);
2766 SecItemClass itemClass
= (SecItemClass
) 0;
2767 CFTypeID itemTypeID
= CFGetTypeID(itemRef
);
2768 if (itemTypeID
== SecIdentityGetTypeID() || itemTypeID
== SecCertificateGetTypeID()) {
2769 // Identities and certificates have the same underlying item class
2770 itemClass
= kSecCertificateItemClass
;
2772 else if (itemTypeID
== SecKeychainItemGetTypeID()) {
2773 // Reference to item in a keychain
2774 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2776 else if (itemTypeID
== SecKeyGetTypeID()) {
2777 // SecKey that isn't stored in a keychain
2778 // %%% will need to change this code when SecKey is no longer CSSM-based %%%
2779 const CSSM_KEY
*cssmKey
;
2780 status
= SecKeyGetCSSMKey((SecKeyRef
)itemRef
, &cssmKey
);
2781 if (status
== errSecSuccess
) {
2782 if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PUBLIC_KEY
)
2783 itemClass
= kSecPublicKeyItemClass
;
2784 else if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PRIVATE_KEY
)
2785 itemClass
= kSecPrivateKeyItemClass
;
2787 itemClass
= kSecSymmetricKeyItemClass
;
2791 if (itemClass
!= 0) {
2792 if (result
!= 0 && result
!= itemClass
) {
2793 return (SecItemClass
) 0; // different item classes in list; bail out
2803 // SecItemParams contains a validated set of input parameters, as well as a
2804 // search reference and attribute list built from those parameters. It is
2805 // designed to be allocated with _CreateSecItemParamsFromDictionary, and
2806 // freed with _FreeSecItemParams.
2808 struct SecItemParams
{
2809 CFDictionaryRef query
; // caller-supplied query
2810 int numResultTypes
; // number of result types requested
2811 int maxMatches
; // max number of matches to return
2812 uint32 keyUsage
; // key usage(s) requested
2813 Boolean returningAttributes
; // true if returning attributes dictionary
2814 Boolean returningData
; // true if returning item's data
2815 Boolean returningRef
; // true if returning item reference
2816 Boolean returningPersistentRef
; // true if returing a persistent reference
2817 Boolean returnAllMatches
; // true if we should return all matches
2818 Boolean returnIdentity
; // true if we are returning a SecIdentityRef
2819 Boolean trustedOnly
; // true if we only return trusted certs
2820 Boolean issuerAndSNToMatch
; // true if both issuer and SN were provided
2821 SecItemClass itemClass
; // item class for this query
2822 SecPolicyRef policy
; // value for kSecMatchPolicy (may be NULL)
2823 SecKeychainRef keychain
; // value for kSecUseKeychain (may be NULL)
2824 CFArrayRef useItems
; // value for kSecUseItemList (may be NULL)
2825 CFArrayRef itemList
; // value for kSecMatchItemList (may be NULL)
2826 CFTypeRef searchList
; // value for kSecMatchSearchList (may be NULL)
2827 CFTypeRef matchLimit
; // value for kSecMatchLimit (may be NULL)
2828 CFTypeRef emailAddrToMatch
; // value for kSecMatchEmailAddressIfPresent (may be NULL)
2829 CFTypeRef validOnDate
; // value for kSecMatchValidOnDate (may be NULL)
2830 CFTypeRef keyClass
; // value for kSecAttrKeyClass (may be NULL)
2831 CFTypeRef service
; // value for kSecAttrService (may be NULL)
2832 CFTypeRef issuer
; // value for kSecAttrIssuer (may be NULL)
2833 CFTypeRef matchIssuers
; // value for kSecMatchIssuers (may be NULL)
2834 CFTypeRef serialNumber
; // value for kSecAttrSerialNumber (may be NULL)
2835 CFTypeRef search
; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2836 CFTypeRef assumedKeyClass
; // if no kSecAttrKeyClass provided, holds the current class we're searching for
2837 CFIndex itemListIndex
; // if no search reference but we have itemList, holds index of next item to return
2838 SecKeychainAttributeList
*attrList
; // attribute list for this query
2839 SecAccessRef access
; // access reference (for SecItemAdd only, not used to find items)
2840 CFDataRef itemData
; // item data (for SecItemAdd only, not used to find items)
2841 CFTypeRef itemRef
; // item reference (to find, add, update or delete, depending on context)
2842 SecIdentityRef identityRef
; // identity reference (input as kSecValueRef)
2843 CFDataRef itemPersistentRef
; // item persistent reference (to find, add, update or delete, depending on context)
2844 Boolean isPCSItem
; // true if this query is for a Protected Cloud Storage item
2848 _ValidateDictionaryEntry(CFDictionaryRef dict
, CFTypeRef key
, const void **value
, CFTypeID expectedTypeID
, CFTypeID altTypeID
)
2850 if (!dict
|| !key
|| !value
|| !expectedTypeID
)
2853 if (!CFDictionaryGetValueIfPresent(dict
, key
, value
)) {
2854 // value was not provided for this key (not an error!)
2857 else if (!(*value
)) {
2858 // provided value is NULL (also not an error!)
2859 return errSecSuccess
;
2862 CFTypeID actualTypeID
= CFGetTypeID(*value
);
2863 if (!((expectedTypeID
== actualTypeID
) || (altTypeID
&& altTypeID
== actualTypeID
))) {
2864 // provided value does not have the expected (or alternate) CF type ID
2865 if ((expectedTypeID
== SecKeychainItemGetTypeID()) &&
2866 (actualTypeID
== SecKeyGetTypeID() || actualTypeID
== SecCertificateGetTypeID())) {
2867 // provided value is a "floating" reference which is not yet in a keychain
2869 return errSecSuccess
;
2871 return errSecItemInvalidValue
;
2874 // provided value is OK; retain it
2878 return errSecSuccess
;
2882 _EnsureUserDefaultKeychainIsSearched(SecItemParams
*itemParams
)
2885 CFArrayRef tmpList
= (CFArrayRef
) itemParams
->searchList
;
2887 // search list exists; make it mutable
2888 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2891 // no search list; start with default list
2892 status
= SecKeychainCopySearchList(&tmpList
);
2893 if (!status
&& tmpList
) {
2894 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2898 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2902 SecKeychainRef userKeychain
= NULL
;
2903 status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2904 if (!status
&& userKeychain
) {
2905 if (!CFArrayContainsValue((CFArrayRef
)itemParams
->searchList
,
2906 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)itemParams
->searchList
)), userKeychain
)) {
2907 // user's default keychain isn't currently in the search list, so append it
2908 CFArrayAppendValue((CFMutableArrayRef
)itemParams
->searchList
, userKeychain
);
2910 CFRelease(userKeychain
);
2915 _EnsureUserDefaultKeychainIsTargeted(SecItemParams
*itemParams
)
2917 if (itemParams
->keychain
) {
2918 return; // keychain is already explicitly specified, assume it's correct
2920 SecKeychainRef userKeychain
= NULL
;
2921 OSStatus status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2922 if (!status
&& userKeychain
) {
2923 itemParams
->keychain
= userKeychain
;
2928 _FreeSecItemParams(SecItemParams
*itemParams
)
2933 if (itemParams
->query
) CFRelease(itemParams
->query
);
2934 if (itemParams
->policy
) CFRelease(itemParams
->policy
);
2935 if (itemParams
->keychain
) CFRelease(itemParams
->keychain
);
2936 if (itemParams
->useItems
) CFRelease(itemParams
->useItems
);
2937 if (itemParams
->itemList
) CFRelease(itemParams
->itemList
);
2938 if (itemParams
->searchList
) CFRelease(itemParams
->searchList
);
2939 if (itemParams
->matchLimit
) CFRelease(itemParams
->matchLimit
);
2940 if (itemParams
->emailAddrToMatch
) CFRelease(itemParams
->emailAddrToMatch
);
2941 if (itemParams
->validOnDate
) CFRelease(itemParams
->validOnDate
);
2942 if (itemParams
->keyClass
) CFRelease(itemParams
->keyClass
);
2943 if (itemParams
->service
) CFRelease(itemParams
->service
);
2944 if (itemParams
->issuer
) CFRelease(itemParams
->issuer
);
2945 if (itemParams
->matchIssuers
) CFRelease(itemParams
->matchIssuers
);
2946 if (itemParams
->serialNumber
) CFRelease(itemParams
->serialNumber
);
2947 if (itemParams
->search
) CFRelease(itemParams
->search
);
2948 if (itemParams
->access
) CFRelease(itemParams
->access
);
2949 if (itemParams
->itemData
) CFRelease(itemParams
->itemData
);
2950 if (itemParams
->itemRef
) CFRelease(itemParams
->itemRef
);
2951 if (itemParams
->identityRef
) CFRelease(itemParams
->identityRef
);
2952 if (itemParams
->itemPersistentRef
) CFRelease(itemParams
->itemPersistentRef
);
2954 _FreeAttrList(itemParams
->attrList
);
2959 static SecItemParams
*
2960 _CreateSecItemParamsFromDictionary(CFDictionaryRef dict
, OSStatus
*error
)
2963 CFTypeRef value
= NULL
;
2964 CFDictionaryRef policyDict
= NULL
;
2965 SecItemParams
*itemParams
= (SecItemParams
*)calloc(1, sizeof(struct SecItemParams
));
2967 require_action(itemParams
!= NULL
, error_exit
, status
= errSecAllocate
);
2968 require_action(dict
&& (CFDictionaryGetTypeID() == CFGetTypeID(dict
)), error_exit
, status
= errSecParam
);
2970 itemParams
->query
= (CFDictionaryRef
) CFRetain(dict
);
2972 // validate input search parameters
2973 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchPolicy
, (const void **)&itemParams
->policy
, SecPolicyGetTypeID(), NULL
), error_exit
);
2974 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchSearchList
, (const void **)&itemParams
->searchList
, CFArrayGetTypeID(), NULL
), error_exit
);
2975 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchItemList
, (const void **)&itemParams
->itemList
, CFArrayGetTypeID(), NULL
), error_exit
);
2976 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchEmailAddressIfPresent
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
2977 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchValidOnDate
, (const void **)&itemParams
->validOnDate
, CFDateGetTypeID(), CFNullGetTypeID()), error_exit
);
2978 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchLimit
, (const void **)&itemParams
->matchLimit
, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit
);
2980 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseItemList
, (const void **)&itemParams
->useItems
, CFArrayGetTypeID(), NULL
), error_exit
);
2981 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseKeychain
, (const void **)&itemParams
->keychain
, SecKeychainGetTypeID(), NULL
), error_exit
);
2983 // validate a subset of input attributes (used to create an appropriate search reference)
2984 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrIssuer
, (const void **)&itemParams
->issuer
, CFDataGetTypeID(), NULL
), error_exit
);
2985 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrSerialNumber
, (const void **)&itemParams
->serialNumber
, CFDataGetTypeID(), NULL
), error_exit
);
2986 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrService
, (const void **)&itemParams
->service
, CFStringGetTypeID(), NULL
), error_exit
);
2987 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrKeyClass
, (const void **)&itemParams
->keyClass
, CFStringGetTypeID(), NULL
), error_exit
);
2989 if (itemParams
->service
&& CFStringHasPrefix((CFStringRef
)itemParams
->service
, CFSTR("ProtectedCloudStorage"))) {
2990 itemParams
->isPCSItem
= true;
2991 if (!SecItemSynchronizable(dict
)) {
2992 _EnsureUserDefaultKeychainIsSearched(itemParams
); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
2993 _EnsureUserDefaultKeychainIsTargeted(itemParams
); // for SecItemAdd
2997 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
2998 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueData
, (const void **)&itemParams
->itemData
, CFDataGetTypeID(), CFStringGetTypeID()), error_exit
);
2999 if (itemParams
->itemData
&& CFGetTypeID(itemParams
->itemData
) == CFStringGetTypeID()) {
3000 /* If we got a string, convert it into a data object */
3001 CFStringRef string
= (CFStringRef
)itemParams
->itemData
;
3002 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(string
), kCFStringEncodingUTF8
) + 1;
3003 CFMutableDataRef data
= CFDataCreateMutable(NULL
, maxLength
);
3004 require_action(data
, error_exit
, status
= errSecAllocate
);
3006 CFDataSetLength(data
, maxLength
);
3008 if (!CFStringGetCString(string
, (char *)CFDataGetMutableBytePtr(data
), maxLength
, kCFStringEncodingUTF8
)) {
3010 status
= errSecAllocate
;
3014 CFDataSetLength(data
, strlen((const char *)CFDataGetBytePtr(data
))); /* dont include NUL in string */
3015 itemParams
->itemData
= data
;
3019 // validate item references
3020 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueRef
, (const void **)&itemParams
->itemRef
, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit
);
3021 if (itemParams
->itemRef
&& (CFGetTypeID(itemParams
->itemRef
) == SecIdentityGetTypeID())) {
3022 itemParams
->identityRef
= (SecIdentityRef
)itemParams
->itemRef
;
3023 itemParams
->itemRef
= NULL
;
3024 SecIdentityCopyCertificate(itemParams
->identityRef
, (SecCertificateRef
*)&itemParams
->itemRef
);
3026 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValuePersistentRef
, (const void **)&itemParams
->itemPersistentRef
, CFDataGetTypeID(), NULL
), error_exit
);
3027 if (itemParams
->itemRef
|| itemParams
->itemPersistentRef
) {
3028 // Caller is trying to add or find an item by reference.
3029 // The supported method for doing that is to provide a kSecUseItemList array
3030 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
3031 // so add the item reference to those arrays here.
3032 if (itemParams
->useItems
) {
3033 CFArrayRef tmpItems
= itemParams
->useItems
;
3034 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3035 CFRelease(tmpItems
);
3037 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3039 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemRef
);
3040 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemPersistentRef
);
3042 if (itemParams
->itemList
) {
3043 CFArrayRef tmpItems
= itemParams
->itemList
;
3044 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3045 CFRelease(tmpItems
);
3047 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3049 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemRef
);
3050 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemPersistentRef
);
3053 // must have an explicit item class, unless one of the following is true:
3054 // - we have an item list to add or search (kSecUseItemList)
3055 // - we have an item reference or persistent reference for the thing we want to look up
3056 // Note that both of these cases will set itemParams->useItems.
3057 // If we have an item list to match (kSecMatchItemList), that still requires an item class,
3058 // so we can perform a search and see if the results match items in the list.
3060 if (!CFDictionaryGetValueIfPresent(dict
, kSecClass
, (const void**) &value
) && !itemParams
->useItems
) {
3061 require_action(false, error_exit
, status
= errSecItemClassMissing
);
3064 itemParams
->itemClass
= _ConvertItemClass(value
, itemParams
->keyClass
, &itemParams
->returnIdentity
);
3065 if (itemParams
->itemClass
== kSecSymmetricKeyItemClass
&& !itemParams
->keyClass
) {
3066 // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
3067 itemParams
->itemClass
= kSecSymmetricKeyItemClass
;
3068 itemParams
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3070 require_action(!(itemParams
->itemClass
== 0 && !itemParams
->useItems
), error_exit
, status
= errSecItemClassMissing
);
3073 // kSecMatchIssuers is only permitted with identities or certificates.
3074 // Convert the input issuers to normalized form.
3075 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchIssuers
, (const void **)&itemParams
->matchIssuers
, CFArrayGetTypeID(), NULL
), error_exit
);
3076 if (itemParams
->matchIssuers
) {
3077 CFTypeRef allowCerts
= CFDictionaryGetValue(itemParams
->query
, kSecUseCertificatesWithMatchIssuers
);
3078 require_action(itemParams
->returnIdentity
|| (allowCerts
&& CFEqual(allowCerts
, kCFBooleanTrue
)), error_exit
, status
= errSecParam
);
3079 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3080 CFArrayRef issuers
= (CFArrayRef
)itemParams
->matchIssuers
;
3081 if (canonical_issuers
) {
3082 CFIndex i
, count
= CFArrayGetCount(issuers
);
3083 for (i
= 0; i
< count
; i
++) {
3084 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(issuers
, i
);
3085 CFDataRef issuer_canonical
= NULL
;
3086 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
3087 issuer_canonical
= SecDistinguishedNameCopyNormalizedSequence((CFDataRef
)issuer_data
);
3088 if (issuer_canonical
) {
3089 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
3090 CFRelease(issuer_canonical
);
3093 if (CFArrayGetCount(canonical_issuers
) > 0) {
3094 CFAssignRetained(itemParams
->matchIssuers
, canonical_issuers
);
3096 CFRelease(canonical_issuers
);
3100 itemParams
->keyUsage
= _CssmKeyUsageFromQuery(dict
);
3101 itemParams
->trustedOnly
= CFDictionaryGetValueIfPresent(dict
, kSecMatchTrustedOnly
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3102 itemParams
->issuerAndSNToMatch
= (itemParams
->issuer
!= NULL
&& itemParams
->serialNumber
!= NULL
);
3104 // other input attributes, used for SecItemAdd but not for finding items
3105 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrAccess
, (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3106 if (itemParams
->access
== NULL
) {
3107 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3108 require_noerr(status
= _ValidateDictionaryEntry(dict
, CFSTR("kSecAttrAccess"), (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3111 // determine how to return the result
3112 itemParams
->numResultTypes
= 0;
3113 itemParams
->returningRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3114 if (itemParams
->returningRef
) ++itemParams
->numResultTypes
;
3115 itemParams
->returningPersistentRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnPersistentRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3116 if (itemParams
->returningPersistentRef
) ++itemParams
->numResultTypes
;
3117 itemParams
->returningAttributes
= CFDictionaryGetValueIfPresent(dict
, kSecReturnAttributes
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3118 if (itemParams
->returningAttributes
) ++itemParams
->numResultTypes
;
3119 itemParams
->returningData
= CFDictionaryGetValueIfPresent(dict
, kSecReturnData
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3120 if (itemParams
->returningData
) ++itemParams
->numResultTypes
;
3122 // default is kSecReturnRef if no result types were specified
3123 if (!itemParams
->numResultTypes
) {
3124 itemParams
->returningRef
= TRUE
;
3125 itemParams
->numResultTypes
= 1;
3128 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3129 itemParams
->maxMatches
= 1;
3130 itemParams
->returnAllMatches
= FALSE
;
3131 if (itemParams
->matchLimit
) {
3132 if (CFStringGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3133 itemParams
->returnAllMatches
= CFEqual(kSecMatchLimitAll
, itemParams
->matchLimit
);
3135 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3136 CFNumberGetValue((CFNumberRef
)itemParams
->matchLimit
, kCFNumberIntType
, &itemParams
->maxMatches
);
3137 require_action(!(itemParams
->maxMatches
< 0), error_exit
, status
= errSecMatchLimitUnsupported
);
3140 if (itemParams
->returnAllMatches
) {
3141 itemParams
->maxMatches
= INT32_MAX
;
3142 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3143 if ((itemParams
->itemClass
==kSecInternetPasswordItemClass
|| itemParams
->itemClass
==kSecGenericPasswordItemClass
) && itemParams
->returningData
)
3144 status
= errSecReturnDataUnsupported
;
3145 require_noerr(status
, error_exit
);
3148 // if we already have an item list (to add or find items in), we don't need a search reference
3149 if (itemParams
->useItems
) {
3150 if (itemParams
->itemClass
== 0) {
3151 itemParams
->itemClass
= _ItemClassFromItemList(itemParams
->useItems
);
3154 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3155 if (itemParams
->itemClass
!= 0) {
3156 status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
);
3158 status
= errSecSuccess
;
3160 goto error_exit
; // all done here
3163 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3164 require_noerr(status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
), error_exit
);
3166 // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
3167 if(itemParams
->policy
) {
3168 policyDict
= SecPolicyCopyProperties(itemParams
->policy
);
3169 CFStringRef oidStr
= (CFStringRef
) CFDictionaryGetValue(policyDict
, kSecPolicyOid
);
3170 if(oidStr
&& CFStringCompare(kSecPolicyAppleSMIME
,oidStr
,0) == 0) {
3171 require_noerr(status
= _ValidateDictionaryEntry(policyDict
, kSecPolicyName
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
3173 CFReleaseNull(policyDict
);
3176 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3177 if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->emailAddrToMatch
) {
3178 // searching for certificates by email address
3179 char *nameBuf
= (char*)malloc(MAXPATHLEN
);
3181 status
= errSecAllocate
;
3183 else if (CFStringGetCString((CFStringRef
)itemParams
->emailAddrToMatch
, nameBuf
, (CFIndex
)MAXPATHLEN
-1, kCFStringEncodingUTF8
)) {
3184 status
= SecKeychainSearchCreateForCertificateByEmail(itemParams
->searchList
, (const char *)nameBuf
, (SecKeychainSearchRef
*)&itemParams
->search
);
3187 status
= errSecItemInvalidValue
;
3189 if (nameBuf
) free(nameBuf
);
3191 else if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->issuerAndSNToMatch
) {
3192 // searching for certificates by issuer and serial number
3193 status
= SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams
->searchList
,
3194 (CFDataRef
)itemParams
->issuer
,
3195 (CFDataRef
)itemParams
->serialNumber
,
3196 (SecKeychainSearchRef
*)&itemParams
->search
);
3198 else if (itemParams
->returnIdentity
&& itemParams
->policy
) {
3199 // searching for identities by policy
3200 status
= SecIdentitySearchCreateWithPolicy(itemParams
->policy
,
3201 (CFStringRef
)itemParams
->service
,
3202 itemParams
->keyUsage
,
3203 itemParams
->searchList
,
3204 itemParams
->trustedOnly
,
3205 (SecIdentitySearchRef
*)&itemParams
->search
);
3207 else if (itemParams
->returnIdentity
) {
3208 // searching for identities
3209 status
= SecIdentitySearchCreate(itemParams
->searchList
,
3210 itemParams
->keyUsage
,
3211 (SecIdentitySearchRef
*)&itemParams
->search
);
3214 // normal keychain item search
3215 status
= SecKeychainSearchCreateFromAttributes(itemParams
->searchList
,
3216 itemParams
->itemClass
,
3217 (itemParams
->attrList
->count
== 0) ? NULL
: itemParams
->attrList
,
3218 (SecKeychainSearchRef
*)&itemParams
->search
);
3222 CFReleaseNull(policyDict
);
3224 _FreeSecItemParams(itemParams
);
3237 SecKeychainRef keychainRef
,
3238 SecAccessRef accessRef
,
3239 SecKeychainAttributeList
*attrList
,
3240 SecKeychainItemRef
*outItemRef
)
3244 // We must specify the access, since a free-floating key won't have one yet by default
3245 SecPointer
<Access
> access
;
3247 access
= Access::required(accessRef
);
3250 CFStringRef descriptor
= NULL
;
3252 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
3253 SecKeychainAttribute attr
= attrList
->attr
[index
];
3254 if (attr
.tag
== kSecKeyPrintName
) {
3255 descriptor
= CFStringCreateWithBytes(NULL
, (const UInt8
*)attr
.data
, attr
.length
, kCFStringEncodingUTF8
, FALSE
);
3260 if (descriptor
== NULL
) {
3261 descriptor
= (CFStringRef
) CFRetain(CFSTR("<unknown>"));
3263 access
= new Access(cfString(descriptor
));
3264 CFRelease(descriptor
);
3267 KeyItem
*key
= KeyItem::required(keyRef
);
3268 Item item
= key
->importTo(Keychain::optional(keychainRef
), access
, attrList
);
3270 *outItemRef
= item
->handle();
3276 _FilterWithPolicy(SecPolicyRef policy
, CFDateRef date
, SecCertificateRef cert
)
3278 CFDictionaryRef props
= NULL
;
3279 CFArrayRef keychains
= NULL
;
3280 CFArrayRef anchors
= NULL
;
3281 CFArrayRef certs
= NULL
;
3282 CFArrayRef chain
= NULL
;
3283 SecTrustRef trust
= NULL
;
3285 SecTrustResultType trustResult
;
3286 Boolean needChain
= false;
3287 Boolean needCSEKU
= false;
3289 if (!policy
|| !cert
) return errSecParam
;
3291 certs
= CFArrayCreate(NULL
, (const void **)&cert
, (CFIndex
)1, &kCFTypeArrayCallBacks
);
3292 status
= SecTrustCreateWithCertificates(certs
, policy
, &trust
);
3293 if(status
) goto cleanup
;
3295 /* Set evaluation date, if specified (otherwise current date is implied) */
3296 if (date
&& (CFGetTypeID(date
) == CFDateGetTypeID())) {
3297 status
= SecTrustSetVerifyDate(trust
, date
);
3298 if(status
) goto cleanup
;
3301 /* Check whether we can avoid full chain evaluation, based on policy */
3302 props
= SecPolicyCopyProperties(policy
);
3304 CFTypeRef oid
= (CFTypeRef
) CFDictionaryGetValue(props
, kSecPolicyOid
);
3305 if (oid
&& (CFEqual(oid
, kSecPolicyAppleX509Basic
) ||
3306 CFEqual(oid
, kSecPolicyAppleRevocation
))) {
3308 } else if (oid
&& (CFEqual(oid
, kSecPolicyAppleCodeSigning
))) {
3313 /* If a code signing EKU purpose is needed, filter out certs without it here
3314 to reduce log noise associated with evaluation failures. */
3316 CFDataRef eku
= CFDataCreate(kCFAllocatorDefault
,
3317 oidExtendedKeyUsageCodeSigning
.data
,
3318 oidExtendedKeyUsageCodeSigning
.length
);
3320 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, eku
)) {
3326 status
= errSecCertificateCannotOperate
;
3332 status
= SecTrustEvaluateLeafOnly(trust
, &trustResult
);
3334 status
= SecTrustEvaluate(trust
, &trustResult
);
3337 if (!(trustResult
== kSecTrustResultProceed
||
3338 trustResult
== kSecTrustResultUnspecified
||
3339 trustResult
== kSecTrustResultRecoverableTrustFailure
)) {
3340 /* The evaluation failed in a non-recoverable way */
3341 status
= errSecCertificateCannotOperate
;
3345 /* If there are no per-cert policy status codes,
3346 * and the cert has not expired, consider it valid for the policy.
3349 (void)SecTrustGetCssmResultCode(trust
, &status
);
3353 if(props
) CFRelease(props
);
3354 if(chain
) CFRelease(chain
);
3355 if(anchors
) CFRelease(anchors
);
3356 if(keychains
) CFRelease(keychains
);
3357 if(certs
) CFRelease(certs
);
3358 if(trust
) CFRelease(trust
);
3364 _FilterWithDate(CFTypeRef validOnDate
, SecCertificateRef cert
)
3366 if (!validOnDate
|| !cert
) return errSecParam
;
3368 CFAbsoluteTime at
, nb
, na
;
3369 if (CFGetTypeID(validOnDate
) == CFDateGetTypeID())
3370 at
= CFDateGetAbsoluteTime((CFDateRef
)validOnDate
);
3372 at
= CFAbsoluteTimeGetCurrent();
3374 OSStatus status
= errSecSuccess
;
3375 nb
= SecCertificateNotValidBefore(cert
);
3376 na
= SecCertificateNotValidAfter(cert
);
3378 if (nb
== 0 || na
== 0 || nb
== na
)
3379 status
= errSecCertificateCannotOperate
;
3381 status
= errSecCertificateNotValidYet
;
3383 status
= errSecCertificateExpired
;
3389 _FilterWithTrust(Boolean trustedOnly
, SecCertificateRef cert
)
3391 if (!cert
) return errSecParam
;
3392 if (!trustedOnly
) return errSecSuccess
;
3394 CFArrayRef certArray
= CFArrayCreate(NULL
, (const void**)&cert
, 1, &kCFTypeArrayCallBacks
);
3395 SecPolicyRef policy
= SecPolicyCreateWithOID(kSecPolicyAppleX509Basic
);
3396 OSStatus status
= (policy
== NULL
) ? errSecPolicyNotFound
: errSecSuccess
;
3399 SecTrustRef trust
= NULL
;
3400 status
= SecTrustCreateWithCertificates(certArray
, policy
, &trust
);
3402 SecTrustResultType trustResult
;
3403 status
= SecTrustEvaluate(trust
, &trustResult
);
3405 if (!(trustResult
== kSecTrustResultProceed
|| trustResult
== kSecTrustResultUnspecified
)) {
3406 status
= (trustResult
== kSecTrustResultDeny
) ? errSecTrustSettingDeny
: errSecNotTrusted
;
3414 CFRelease(certArray
);
3420 static bool items_matching_issuer_parent(CFDataRef issuer
, CFArrayRef issuers
, int recurse
) {
3421 if (!issuers
|| CFArrayGetCount(issuers
) == 0) { return false; }
3423 /* We found a match, we're done. */
3424 if (CFArrayContainsValue(issuers
, CFRangeMake(0, CFArrayGetCount(issuers
)), issuer
)) { return true; }
3426 /* Prevent infinite recursion */
3427 if (recurse
<= 0) { return false; }
3430 /* Query for parents */
3431 CFMutableDictionaryRef query
= NULL
;
3432 CFTypeRef parents
= NULL
;
3435 require_quiet(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 4, &kCFTypeDictionaryKeyCallBacks
,
3436 &kCFTypeDictionaryValueCallBacks
), out
);
3437 CFDictionaryAddValue(query
, kSecClass
, kSecClassCertificate
);
3438 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
3439 CFDictionaryAddValue(query
, kSecAttrSubject
, issuer
);
3440 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
3441 require_noerr_quiet(SecItemCopyMatching(query
, &parents
), out
);
3443 if (parents
&& CFArrayGetTypeID() == CFGetTypeID(parents
)) {
3444 CFIndex i
, count
= CFArrayGetCount((CFArrayRef
)parents
);
3445 for (i
= 0; i
< count
; i
++) {
3446 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex((CFArrayRef
)parents
, i
);
3447 CFDataRef cert_issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3448 if (CFEqual(cert_issuer
, issuer
)) {
3449 // Self-issued cert, don't look for parents.
3450 CFReleaseNull(cert_issuer
);
3453 found
= items_matching_issuer_parent(cert_issuer
, issuers
, recurse
);
3454 CFReleaseNull(cert_issuer
);
3455 if (found
) { break; }
3457 } else if (parents
&& SecCertificateGetTypeID() == CFGetTypeID(parents
)) {
3458 SecCertificateRef cert
= (SecCertificateRef
)parents
;
3459 CFDataRef cert_issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3460 require_action_quiet(!CFEqual(cert_issuer
, issuer
), out
, CFReleaseNull(cert_issuer
));
3461 found
= items_matching_issuer_parent(cert_issuer
, issuers
, recurse
);
3462 CFReleaseNull(cert_issuer
);
3466 CFReleaseNull(query
);
3467 CFReleaseNull(parents
);
3472 _FilterWithIssuers(CFArrayRef issuers
, SecCertificateRef cert
)
3474 if (!issuers
|| CFArrayGetCount(issuers
) == 0) return errSecParam
;
3475 if (!cert
) return errSecParam
;
3477 OSStatus status
= errSecInternalError
;
3479 /* kSecMatchIssuers matches certificates where ANY certificate in the chain has this issuer.
3480 * So we now need to recursively query the keychain for this cert's parents to determine if
3481 * they match. (This is why we limited the use of this key in _CreateSecItemParamsFromDictionary.) */
3482 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3483 if (items_matching_issuer_parent(issuer
, issuers
, 10)) {
3484 status
= errSecSuccess
;
3487 CFReleaseNull(issuer
);
3491 static SecKeychainItemRef
3492 CopyResolvedKeychainItem(CFTypeRef item
)
3494 SecKeychainItemRef kcItem
= NULL
;
3495 OSStatus status
= errSecSuccess
;
3497 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
3498 // persistent reference, resolve first
3499 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &kcItem
);
3503 kcItem
= (SecKeychainItemRef
) CFRetain(item
);
3506 // ask for the item's class:
3507 // will return an error if the item has been deleted
3508 SecItemClass itemClass
;
3509 SecCertificateRef certRef
= NULL
;
3510 CFTypeID itemTypeID
= CFGetTypeID(kcItem
);
3511 if (itemTypeID
== SecIdentityGetTypeID()) {
3512 status
= SecIdentityCopyCertificate((SecIdentityRef
)kcItem
, &certRef
);
3514 else if (itemTypeID
== SecCertificateGetTypeID()) {
3515 certRef
= (SecCertificateRef
) CFRetain(kcItem
);
3518 // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef
3519 itemClass
= kSecCertificateItemClass
;
3522 status
= SecKeychainItemCopyAttributesAndData(kcItem
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3537 UpdateKeychainSearchAndCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3539 // This function refreshes the search parameters in the specific case where
3540 // the caller is searching for kSecClassKey items but did not provide the
3541 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3542 // we must perform separate searches to obtain all results.
3544 OSStatus status
= errSecItemNotFound
;
3545 if (!params
|| !params
->assumedKeyClass
|| !params
->query
|| !item
)
3548 // Free the previous search reference and attribute list.
3550 CFRelease(params
->search
);
3551 params
->search
= NULL
;
3552 _FreeAttrList(params
->attrList
);
3553 params
->attrList
= NULL
;
3555 // Make a copy of the query dictionary so we can set the key class parameter.
3556 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(NULL
, 0, params
->query
);
3557 CFRelease(params
->query
);
3558 params
->query
= dict
;
3559 CFDictionarySetValue(dict
, kSecAttrKeyClass
, params
->assumedKeyClass
);
3561 // Determine the current item class for this search, and the next assumed key class.
3562 if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassSymmetric
)) {
3563 params
->itemClass
= kSecSymmetricKeyItemClass
;
3564 params
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3565 } else if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassPublic
)) {
3566 params
->itemClass
= kSecPublicKeyItemClass
;
3567 params
->assumedKeyClass
= kSecAttrKeyClassPrivate
;
3569 params
->itemClass
= kSecPrivateKeyItemClass
;
3570 params
->assumedKeyClass
= NULL
;
3573 // Rebuild the attribute list for the new key class.
3574 if (_CreateSecKeychainAttributeListFromDictionary(dict
, params
->itemClass
, ¶ms
->attrList
) == errSecSuccess
) {
3575 // Create a new search reference for the new attribute list.
3576 if (SecKeychainSearchCreateFromAttributes(params
->searchList
,
3578 (params
->attrList
->count
== 0) ? NULL
: params
->attrList
,
3579 (SecKeychainSearchRef
*)¶ms
->search
) == errSecSuccess
) {
3580 // Return the first matching item from the new search.
3581 // We won't come back here again until there are no more matching items for this search.
3582 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)params
->search
, (SecKeychainItemRef
*)item
);
3590 SecItemSearchCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3592 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3593 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3594 // depending on the type of search reference.
3597 CFTypeRef search
= (params
) ? params
->search
: NULL
;
3598 CFTypeID typeID
= (search
) ? CFGetTypeID(search
) : 0;
3600 if (search
&& typeID
== SecIdentitySearchGetTypeID()) {
3601 status
= SecIdentitySearchCopyNext((SecIdentitySearchRef
)search
, (SecIdentityRef
*)item
);
3603 else if (search
&& typeID
== SecKeychainSearchGetTypeID()) {
3604 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)search
, (SecKeychainItemRef
*)item
);
3605 // Check if we need to refresh the search for the next key class
3606 while (status
== errSecItemNotFound
&& params
->assumedKeyClass
!= NULL
)
3607 status
= UpdateKeychainSearchAndCopyNext(params
, item
);
3609 else if (typeID
== 0 && params
&& (params
->useItems
|| params
->itemList
)) {
3610 // No search available, but there is an item list available.
3611 // Return the next candidate item from the caller's item list
3612 CFArrayRef itemList
= (params
->useItems
) ? params
->useItems
: params
->itemList
;
3613 CFIndex count
= CFArrayGetCount(itemList
);
3614 *item
= (CFTypeRef
) NULL
;
3615 if (params
->itemListIndex
< count
) {
3616 *item
= (CFTypeRef
)CFArrayGetValueAtIndex(itemList
, params
->itemListIndex
++);
3618 // Potentially resolve persistent item references here, and
3619 // verify the item reference we're about to hand back is still
3620 // valid (it could have been deleted from the keychain while
3621 // our query was holding onto the itemList).
3622 *item
= CopyResolvedKeychainItem(*item
);
3623 if (*item
&& (CFGetTypeID(*item
) == SecIdentityGetTypeID())) {
3624 // Persistent reference resolved to an identity, so return that type.
3625 params
->returnIdentity
= true;
3629 status
= (*item
) ? errSecSuccess
: errSecItemNotFound
;
3632 status
= errSecItemNotFound
;
3638 FilterCandidateItem(CFTypeRef
*item
, SecItemParams
*itemParams
, SecIdentityRef
*identity
)
3640 if (!item
|| *item
== NULL
|| !itemParams
)
3641 return errSecItemNotFound
;
3644 CFStringRef commonName
= NULL
;
3645 SecIdentityRef foundIdentity
= NULL
;
3646 if (CFGetTypeID(*item
) == SecIdentityGetTypeID()) {
3647 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3648 // replace the found "item" with its associated certificate (which is the
3649 // item we actually want for purposes of getting attributes, data, or a
3650 // persistent data reference), and return the identity separately.
3651 SecCertificateRef certificate
;
3652 status
= SecIdentityCopyCertificate((SecIdentityRef
) *item
, &certificate
);
3653 if (itemParams
->returnIdentity
) {
3654 foundIdentity
= (SecIdentityRef
) *item
;
3656 *identity
= foundIdentity
;
3662 *item
= (CFTypeRef
)certificate
;
3665 CFDictionaryRef query
= itemParams
->query
;
3667 if (itemParams
->itemClass
== kSecCertificateItemClass
) {
3668 // perform string comparisons first
3669 CFStringCompareFlags flags
= _StringCompareFlagsFromQuery(query
);
3670 CFStringRef nameContains
, nameStarts
, nameEnds
, nameExact
;
3671 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectContains
, (const void **)&nameContains
))
3672 nameContains
= NULL
;
3673 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&nameStarts
))
3675 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&nameEnds
))
3677 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectWholeString
, (const void **)&nameExact
))
3679 if (nameContains
|| nameStarts
|| nameEnds
|| nameExact
) {
3680 status
= SecCertificateCopyCommonName((SecCertificateRef
)*item
, &commonName
);
3681 if (status
|| !commonName
) goto filterOut
;
3684 CFRange range
= CFStringFind(commonName
, nameContains
, flags
);
3685 if (range
.length
< 1)
3687 // certificate item contains string; proceed to next check
3690 CFRange range
= CFStringFind(commonName
, nameStarts
, flags
);
3691 if (range
.length
< 1 || range
.location
> 1)
3693 // certificate item starts with string; proceed to next check
3696 CFRange range
= CFStringFind(commonName
, nameEnds
, flags
);
3697 if (range
.length
< 1 || range
.location
!= (CFStringGetLength(commonName
) - CFStringGetLength(nameEnds
)))
3699 // certificate item ends with string; proceed to next check
3702 CFRange range
= CFStringFind(commonName
, nameExact
, flags
);
3703 if (range
.length
< 1 || (CFStringGetLength(commonName
) != CFStringGetLength(nameExact
)))
3705 // certificate item exactly matches string; proceed to next check
3707 if (itemParams
->returnIdentity
) {
3708 // if we already found and returned the identity, we can skip this
3709 if (!foundIdentity
) {
3710 status
= SecIdentityCreateWithCertificate(itemParams
->searchList
, (SecCertificateRef
) *item
, identity
);
3711 if (status
) goto filterOut
;
3713 // certificate item is part of an identity; proceed to next check
3715 if (itemParams
->policy
) {
3716 status
= _FilterWithPolicy(itemParams
->policy
, (CFDateRef
)itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3717 if (status
) goto filterOut
;
3718 // certificate item is valid for specified policy (and optionally specified date)
3720 if (itemParams
->validOnDate
) {
3721 status
= _FilterWithDate(itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3722 if (status
) goto filterOut
;
3723 // certificate item is valid for specified date
3725 if (itemParams
->trustedOnly
) {
3726 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3727 // their trust has already been validated and we can skip this part.
3728 if (!(foundIdentity
&& itemParams
->returnIdentity
&& itemParams
->policy
)) {
3729 status
= _FilterWithTrust(itemParams
->trustedOnly
, (SecCertificateRef
) *item
);
3730 if (status
) goto filterOut
;
3732 // certificate item is trusted on this system
3734 if (itemParams
->matchIssuers
) {
3735 status
= _FilterWithIssuers((CFArrayRef
)itemParams
->matchIssuers
, (SecCertificateRef
) *item
);
3736 if (status
) goto filterOut
;
3737 // certificate item has one of the issuers
3740 if (itemParams
->itemList
) {
3741 Boolean foundMatch
= FALSE
;
3742 CFIndex idx
, count
= CFArrayGetCount(itemParams
->itemList
);
3743 for (idx
=0; idx
<count
; idx
++) {
3744 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->itemList
, idx
);
3745 SecKeychainItemRef realItem
= NULL
;
3746 SecCertificateRef aCert
= NULL
;
3747 if (anItem
== NULL
) {
3750 if (CFDataGetTypeID() == CFGetTypeID(anItem
) &&
3751 errSecSuccess
== SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
)) {
3754 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
) &&
3755 errSecSuccess
== SecIdentityCopyCertificate((SecIdentityRef
)anItem
, &aCert
)) {
3758 if (CFEqual(anItem
, (CFTypeRef
) *item
)) {
3765 CFRelease(realItem
);
3771 if (!foundMatch
) goto filterOut
;
3772 // item was found on provided list
3775 if (foundIdentity
&& !identity
) {
3776 CFRelease(foundIdentity
);
3779 CFRelease(commonName
);
3782 // if we get here, consider the item a match
3783 return errSecSuccess
;
3787 CFRelease(commonName
);
3791 if (foundIdentity
) {
3792 CFRelease(foundIdentity
);
3797 return errSecItemNotFound
;
3801 AddItemResults(SecKeychainItemRef item
,
3802 SecIdentityRef identity
,
3803 SecItemParams
*itemParams
,
3804 CFAllocatorRef allocator
,
3805 CFMutableArrayRef
*items
,
3808 // Given a found item (which may also be an identity), this function adds
3809 // the requested result types (specified in itemParams) to the appropriate
3810 // container as follows:
3812 // 1. If there is only one result type (numResultTypes == 1) and only one
3813 // match requested (maxMatches == 1), set *result directly.
3815 // 2. If there are multiple result types (numResultTypes > 1), and only one
3816 // match requested (maxMatches == 1), add each result type to itemDict
3817 // and set itemDict as the value of *result.
3819 // 3. If there is only one result type (numResultTypes == 1) and multiple
3820 // possible matches (maxMatches > 1), add the result type to *items
3821 // and set *items as the value of *result.
3823 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3824 // possible matches (maxMatches > 1), add each result type to itemDict,
3825 // add itemDict to *items, and set *items as the value of *result.
3827 // Note that we allocate *items if needed.
3829 CFTypeRef localResult
= NULL
;
3831 if (!item
|| !itemParams
|| !result
)
3834 if (itemParams
->maxMatches
> 1) {
3835 // if we can return more than one item, we must have an array
3838 else if (*items
== NULL
)
3839 *items
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3842 OSStatus tmpStatus
, status
= errSecSuccess
;
3843 CFMutableArrayRef itemArray
= (items
) ? *items
: NULL
;
3844 CFMutableDictionaryRef itemDict
= NULL
;
3845 if (itemParams
->numResultTypes
> 1) {
3846 // if we're returning more than one result type, each item we return must be a dictionary
3847 itemDict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3850 if (itemParams
->returningRef
) {
3851 const void* itemRef
= (identity
) ? (const void*)identity
: (const void*)item
;
3853 CFDictionaryAddValue(itemDict
, kSecValueRef
, itemRef
);
3855 else if (itemArray
) {
3856 CFArrayAppendValue(itemArray
, itemRef
);
3859 CFReleaseNull(localResult
);
3860 localResult
= CFRetain((CFTypeRef
)itemRef
);
3864 if (itemParams
->returningPersistentRef
) {
3865 CFDataRef persistentRef
;
3866 SecKeychainItemRef tmpItem
= item
;
3867 if (itemParams
->identityRef
) {
3868 tmpItem
= (SecKeychainItemRef
)itemParams
->identityRef
;
3870 tmpStatus
= SecKeychainItemCreatePersistentReference(tmpItem
, &persistentRef
);
3871 if (tmpStatus
== errSecSuccess
) {
3873 CFDictionaryAddValue(itemDict
, kSecValuePersistentRef
, persistentRef
);
3875 else if (itemArray
) {
3876 CFArrayAppendValue(itemArray
, persistentRef
);
3879 CFReleaseNull(localResult
);
3880 localResult
= CFRetain(persistentRef
);
3882 CFRelease(persistentRef
);
3884 else if (status
== errSecSuccess
) {
3889 if (itemParams
->returningData
) {
3890 // Use SecCertificateCopyData if we have a SecCertificateRef item.
3891 // Note that a SecCertificateRef may not actually be a SecKeychainItem,
3892 // in which case SecKeychainItemCopyContent will not obtain its data.
3894 if (CFGetTypeID(item
) == SecCertificateGetTypeID()) {
3895 CFDataRef dataRef
= SecCertificateCopyData((SecCertificateRef
)item
);
3898 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3900 else if (itemArray
) {
3901 CFArrayAppendValue(itemArray
, dataRef
);
3904 CFReleaseNull(localResult
);
3905 localResult
= CFRetain(dataRef
);
3908 status
= errSecSuccess
;
3911 status
= errSecAllocate
;
3917 tmpStatus
= SecKeychainItemCopyContent(item
, NULL
, NULL
, &length
, &data
);
3918 if (tmpStatus
== errSecSuccess
) {
3919 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)data
, length
);
3921 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3923 else if (itemArray
) {
3924 CFArrayAppendValue(itemArray
, dataRef
);
3927 CFReleaseNull(localResult
);
3928 localResult
= CFRetain(dataRef
);
3931 (void) SecKeychainItemFreeContent(NULL
, data
);
3933 else if (status
== errSecSuccess
) {
3939 if (itemParams
->returningAttributes
) {
3940 CFDictionaryRef attrsDict
= NULL
;
3941 SecItemClass itemClass
;
3942 // since we have an item, allow its actual class to override the query-specified item class
3943 tmpStatus
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3945 itemClass
= itemParams
->itemClass
;
3947 tmpStatus
= _CreateAttributesDictionaryFromItem(allocator
, itemClass
, item
, &attrsDict
);
3950 // add all keys and values from attrsDict to the item dictionary
3951 CFDictionaryApplyFunction(attrsDict
, _AddDictValueToOtherDict
, &itemDict
);
3953 else if (itemArray
) {
3954 CFArrayAppendValue(itemArray
, attrsDict
);
3957 CFReleaseNull(localResult
);
3958 localResult
= CFRetain(attrsDict
);
3960 CFRelease(attrsDict
);
3962 if (tmpStatus
&& (status
== errSecSuccess
)) {
3969 CFArrayAppendValue(itemArray
, itemDict
);
3970 CFRelease(itemDict
);
3971 CFReleaseNull(localResult
);
3972 localResult
= itemArray
;
3975 CFReleaseNull(localResult
);
3976 localResult
= itemDict
;
3979 else if (itemArray
) {
3980 CFReleaseNull(localResult
);
3981 localResult
= itemArray
;
3985 *result
= localResult
;
3992 CFDataRef
_SecItemGetPersistentReference(CFTypeRef raw_item
)
3995 Item item
= ItemImpl::required((SecKeychainItemRef
)raw_item
);
3996 return item
->getPersistentRef();
4002 /******************************************************************************/
4003 #pragma mark SecItem API functions
4004 /******************************************************************************/
4007 // Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
4009 static SInt32
readNumber(CFTypeRef obj
) {
4010 CFTypeID tid
= CFGetTypeID(obj
);
4012 if (tid
== CFNumberGetTypeID()) {
4013 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt32Type
, &v
);
4015 } else if (tid
== CFBooleanGetTypeID()) {
4016 v
= CFBooleanGetValue((CFBooleanRef
)obj
);
4018 } else if (tid
== CFStringGetTypeID()) {
4019 v
= CFStringGetIntValue((CFStringRef
)obj
);
4020 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v
);
4021 /* If a string converted to an int isn't equal to the int printed as
4022 a string, return a CFStringRef instead. */
4023 if (!CFEqual(t
, obj
)) {
4034 // Function to check whether the kSecAttrSynchronizable flag is set in the query.
4036 static Boolean
SecItemSynchronizable(CFDictionaryRef query
)
4038 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
);
4039 Boolean result
= (value
&& readNumber(value
));
4045 // Function to check whether a synchronizable persistent reference was provided.
4047 static Boolean
SecItemIsIOSPersistentReference(CFTypeRef value
)
4050 return ::_SecItemParsePersistentRef((CFDataRef
)value
, NULL
, NULL
, NULL
);
4055 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
4058 // Function to find out which keychains are targetted by the query.
4060 static OSStatus
SecItemCategorizeQuery(CFDictionaryRef query
, bool &can_target_ios
, bool &can_target_osx
, bool &useDataProtectionKeychainFlag
)
4062 // By default, target both keychain.
4063 can_target_osx
= can_target_ios
= true;
4064 useDataProtectionKeychainFlag
= false;
4066 // Check no-legacy flag.
4067 // it's iOS or bust if we're on MZ!
4068 CFTypeRef useDataProtection
= NULL
;
4069 if (_CFMZEnabled()) {
4070 useDataProtection
= kCFBooleanTrue
;
4073 // In case your CFDict is dumb and compares by pointer equality we check both versions of the symbol
4074 if (!CFDictionaryGetValueIfPresent(query
, kSecUseDataProtectionKeychain
, &useDataProtection
)) {
4075 // Ah the irony of ignoring deprecation while checking for a legacy-avoiding attribute
4076 #pragma clang diagnostic push
4077 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4078 useDataProtection
= CFDictionaryGetValue(query
, kSecAttrNoLegacy
);
4079 #pragma clang diagnostic pop
4083 if (useDataProtection
!= NULL
) {
4084 useDataProtectionKeychainFlag
= readNumber(useDataProtection
);
4085 can_target_ios
= useDataProtectionKeychainFlag
;
4086 can_target_osx
= !can_target_ios
;
4087 return errSecSuccess
;
4090 // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value.
4091 CFTypeRef value
= CFDictionaryGetValue(query
, kSecValueRef
);
4092 if (value
!= NULL
) {
4093 CFTypeID typeID
= CFGetTypeID(value
);
4094 if (typeID
== SecKeyGetTypeID()) {
4095 can_target_osx
= SecKeyIsCDSAKey((SecKeyRef
)value
);
4096 can_target_ios
= !can_target_osx
;
4097 } else if (typeID
== SecCertificateGetTypeID()) {
4098 // All types of certificates can target OSX keychains, but OSX certificates won't work on iOS
4099 can_target_ios
&= !SecCertificateIsItemImplInstance((SecCertificateRef
)value
);
4100 } else if (typeID
== SecKeychainItemGetTypeID()) {
4101 // SecKeychainItemRef can target iOS keychain only when it has attached iOS-style persistent reference.
4102 if (_SecItemGetPersistentReference(value
) == NULL
) {
4103 can_target_ios
= false;
4108 // Check presence of kSecAttrTokenID and kSecAttrAccessControl; they are not defined for CDSA keychain.
4109 if (CFDictionaryContainsKey(query
, kSecAttrTokenID
) || CFDictionaryContainsKey(query
, kSecAttrAccessControl
)) {
4110 can_target_osx
= false;
4113 // Check for special token access groups. If present, redirect query to iOS keychain.
4114 value
= CFDictionaryGetValue(query
, kSecAttrAccessGroup
);
4115 if (value
!= NULL
&& CFEqual(value
, kSecAttrAccessGroupToken
)) {
4116 can_target_osx
= false;
4119 // Synchronizable items should go to iOS keychain only.
4120 if (SecItemSynchronizable(query
)) {
4121 can_target_osx
= false;
4124 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
4125 if (value
!= NULL
) {
4126 if (SecItemIsIOSPersistentReference(value
)) {
4127 can_target_osx
= false;
4129 // Non-iOS-style persistent references should not be fed to iOS keychain queries.
4130 can_target_ios
= false;
4134 // Presence of following atributes means that query is OSX-only.
4135 static const CFStringRef
*osx_only_items
[] = {
4137 &kSecMatchSearchList
,
4138 &kSecMatchSubjectStartsWith
,
4139 &kSecMatchSubjectEndsWith
,
4140 &kSecMatchSubjectWholeString
,
4141 &kSecMatchDiacriticInsensitive
,
4142 &kSecMatchWidthInsensitive
,
4151 for (CFIndex i
= 0; i
< array_size(osx_only_items
); i
++) {
4152 can_target_ios
= can_target_ios
&& !CFDictionaryContainsKey(query
, *osx_only_items
[i
]);
4155 // Absence of all of kSecItemClass, kSecValuePersistentRef, and kSecValueRef means that the query can't target iOS
4156 if(CFDictionaryGetValue(query
, kSecClass
) == NULL
&&
4157 CFDictionaryGetValue(query
, kSecValuePersistentRef
) == NULL
&&
4158 CFDictionaryGetValue(query
, kSecValueRef
) == NULL
) {
4159 can_target_ios
= false;
4162 return (can_target_ios
|| can_target_osx
) ? errSecSuccess
: errSecParam
;
4166 // Function to check whether the kSecAttrSynchronizable attribute is being updated.
4168 static Boolean
SecItemHasSynchronizableUpdate(Boolean synchronizable
, CFDictionaryRef changes
)
4170 CFTypeRef newValue
= CFDictionaryGetValue(changes
, kSecAttrSynchronizable
);
4174 Boolean new_sync
= readNumber(newValue
);
4175 Boolean old_sync
= synchronizable
;
4177 return (old_sync
!= new_sync
);
4181 // Function to apply changes to a mutable dictionary.
4182 // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
4184 static void SecItemApplyChanges(const void *key
, const void *value
, void *context
)
4186 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
) context
;
4189 CFDictionarySetValue(dict
, key
, value
);
4193 // Function to change matching items from non-syncable to syncable
4194 // (if toSyncable is true), otherwise from syncable to non-syncable.
4195 // This currently moves items between keychain containers.
4197 static OSStatus
SecItemChangeSynchronizability(CFDictionaryRef query
, CFDictionaryRef changes
, Boolean toSyncable
)
4199 // Note: the input query dictionary is a mutable copy of the query originally
4200 // provided by the caller as the first parameter to SecItemUpdate. It may not
4201 // specify returning attributes or data, but we will need both to make a copy.
4203 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnRef
);
4204 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnPersistentRef
);
4205 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnData
);
4206 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnAttributes
, kCFBooleanTrue
);
4207 if (NULL
== CFDictionaryGetValue(changes
, kSecValueData
))
4208 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnData
, kCFBooleanTrue
);
4213 status
= SecItemCopyMatching_osx(query
, &result
);
4215 status
= SecItemCopyMatching_ios(query
, &result
);
4220 return errSecItemNotFound
;
4222 CFMutableArrayRef items
;
4223 if (CFGetTypeID(result
) != CFArrayGetTypeID()) {
4224 items
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4225 CFArrayAppendValue(items
, result
);
4229 items
= (CFMutableArrayRef
)result
;
4232 CFIndex idx
, count
= (items
) ? CFArrayGetCount(items
) : 0;
4233 int priority
= LOG_DEBUG
;
4235 for (idx
= 0; idx
< count
; idx
++) {
4236 CFDictionaryRef dict
= (CFDictionaryRef
) CFArrayGetValueAtIndex(items
, idx
);
4237 CFMutableDictionaryRef item
= (CFMutableDictionaryRef
)
4238 SecItemCopyTranslatedAttributes(dict
,
4239 CFDictionaryGetValue(query
, kSecClass
),
4240 (toSyncable
) ? true : false /*iOSOut*/,
4241 true /*pruneMatch*/,
4243 true /*pruneReturn*/,
4244 false /*pruneData*/,
4245 (toSyncable
) ? true : false /*pruneAccess*/);
4246 // hold onto the query before applying changes, in case the item already exists.
4247 // note that we cannot include the creation or modification dates from our
4248 // found item in this query, as they may not match the item in the other keychain.
4249 CFMutableDictionaryRef itemQuery
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
4250 CFDictionaryRemoveValue(itemQuery
, kSecAttrCreationDate
);
4251 CFDictionaryRemoveValue(itemQuery
, kSecAttrModificationDate
);
4252 // apply changes to the item dictionary that we will pass to SecItemAdd
4253 CFDictionaryApplyFunction(changes
, SecItemApplyChanges
, item
);
4255 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4256 status
= SecItemAdd_ios(item
, NULL
);
4257 secitemlog(priority
, "ChangeSync: SecItemAdd_ios=%d", status
);
4258 if (errSecDuplicateItem
== status
) {
4259 // find and apply changes to the existing syncable item.
4260 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4261 status
= SecItemUpdate_ios(itemQuery
, changes
);
4262 secitemlog(priority
, "ChangeSync: SecItemUpdate_ios=%d", status
);
4264 if (errSecSuccess
== status
) {
4265 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4266 status
= SecItemDelete_osx(itemQuery
);
4267 secitemlog(priority
, "ChangeSync: SecItemDelete_osx=%d", status
);
4271 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4272 status
= SecItemAdd_osx(item
, NULL
);
4273 secitemlog(priority
, "ChangeSync: SecItemAdd_osx=%d", status
);
4274 if (errSecDuplicateItem
== status
) {
4275 // find and apply changes to the existing non-syncable item.
4276 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4277 status
= SecItemUpdate_osx(itemQuery
, changes
);
4278 secitemlog(priority
, "ChangeSync: SecItemUpdate_osx=%d", status
);
4280 if (errSecSuccess
== status
) {
4281 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4282 status
= SecItemDelete_ios(itemQuery
);
4283 secitemlog(priority
, "ChangeSync: SecItemDelete_ios=%d", status
);
4286 CFReleaseSafe(item
);
4287 CFReleaseSafe(itemQuery
);
4291 CFReleaseSafe(items
);
4300 SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
) {
4301 CFTypeRef ref
= NULL
;
4302 CFStringRef item_class_string
= (CFStringRef
)CFDictionaryGetValue(refAttributes
, kSecClass
);
4303 SecItemClass item_class
= (SecItemClass
) 0;
4305 if (CFEqual(item_class_string
, kSecClassGenericPassword
)) {
4306 item_class
= kSecGenericPasswordItemClass
;
4307 } else if (CFEqual(item_class_string
, kSecClassInternetPassword
)) {
4308 item_class
= kSecInternetPasswordItemClass
;
4311 if (item_class
!= 0) {
4312 // we carry v_Data around here so the *_ios calls can find it and locate
4313 // their own data. Putting things in the attribute list doesn't help as
4314 // the osx keychainitem and item calls bail when they don't see a keychain
4315 // object. If we need to make them work we either have to bridge them, or
4316 // find a way to craft a workable keychain object. #if'ed code left below
4317 // in case we need to go down that path.
4319 SecKeychainAttributeList attrs
= {};
4320 SecKeychainAttribute attr
= {};
4326 Item item
= Item(item_class
, &attrs
, 0, "");
4327 v
= CFCast(CFData
, CFDictionaryGetValue(refAttributes
, kSecValuePersistentRef
));
4329 item
->setPersistentRef((CFDataRef
)v
);
4331 ref
= item
->handle();
4338 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4339 * is a member of the specified application group, and is signed by Apple.
4342 SecItemValidateAppleApplicationGroupAccess(CFStringRef group
)
4344 SecTrustedApplicationRef app
= NULL
;
4345 SecRequirementRef requirement
= NULL
;
4346 SecCodeRef code
= NULL
;
4347 OSStatus status
= errSecParam
;
4350 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(group
), kCFStringEncodingUTF8
) + 1;
4351 char* buffer
= (char*) malloc(length
);
4353 if (CFStringGetCString(group
, buffer
, length
, kCFStringEncodingUTF8
)) {
4354 status
= SecTrustedApplicationCreateApplicationGroup(buffer
, NULL
, &app
);
4358 status
= errSecMemoryError
;
4362 status
= SecTrustedApplicationCopyRequirement(app
, &requirement
);
4365 status
= SecCodeCopySelf(kSecCSDefaultFlags
, &code
);
4368 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, requirement
);
4371 CFReleaseSafe(code
);
4372 CFReleaseSafe(requirement
);
4377 static Mutex
& gParentCertCacheLock() {
4378 static Mutex fParentCertCacheLock
;
4379 return fParentCertCacheLock
;
4381 static CFMutableDictionaryRef gParentCertCache
;
4382 static CFMutableArrayRef gParentCertCacheList
;
4383 #define PARENT_CACHE_SIZE 100
4385 void SecItemParentCachePurge() {
4386 StLock
<Mutex
> _(gParentCertCacheLock());
4387 CFReleaseNull(gParentCertCache
);
4388 CFReleaseNull(gParentCertCacheList
);
4391 static CFArrayRef CF_RETURNS_RETAINED
parentCacheRead(SecCertificateRef certificate
) {
4392 CFArrayRef parents
= NULL
;
4394 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4395 if (!digest
) return NULL
;
4397 StLock
<Mutex
> _(gParentCertCacheLock());
4398 if (gParentCertCache
&& gParentCertCacheList
) {
4399 if (0 <= (ix
= CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4400 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4402 // Cache hit. Get value and move entry to the top of the list.
4403 parents
= (CFArrayRef
)CFDictionaryGetValue(gParentCertCache
, digest
);
4404 CFArrayRemoveValueAtIndex(gParentCertCacheList
, ix
);
4405 CFArrayAppendValue(gParentCertCacheList
, digest
);
4408 CFRetainSafe(parents
);
4412 static void parentCacheWrite(SecCertificateRef certificate
, CFArrayRef parents
) {
4413 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4414 if (!digest
) return;
4416 StLock
<Mutex
> _(gParentCertCacheLock());
4417 if (!gParentCertCache
|| !gParentCertCacheList
) {
4418 CFReleaseNull(gParentCertCache
);
4419 gParentCertCache
= makeCFMutableDictionary();
4420 CFReleaseNull(gParentCertCacheList
);
4421 gParentCertCacheList
= makeCFMutableArray(0);
4424 if (gParentCertCache
&& gParentCertCacheList
) {
4425 // check to make sure another thread didn't add this entry to the cache already
4426 if (0 > CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4427 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4429 CFDictionaryAddValue(gParentCertCache
, digest
, parents
);
4430 if (PARENT_CACHE_SIZE
<= CFArrayGetCount(gParentCertCacheList
)) {
4431 // Remove least recently used cache entry.
4432 CFDictionaryRemoveValue(gParentCertCache
, CFArrayGetValueAtIndex(gParentCertCacheList
, 0));
4433 CFArrayRemoveValueAtIndex(gParentCertCacheList
, 0);
4435 CFArrayAppendValue(gParentCertCacheList
, digest
);
4441 * SecItemCopyParentCertificates_osx returns an array of zero of more possible
4442 * issuer certificates for the provided certificate. No cryptographic validation
4443 * of the signature is performed in this function; its purpose is only to
4444 * provide a list of candidate certificates.
4447 SecItemCopyParentCertificates_osx(SecCertificateRef certificate
, void *context
)
4449 #pragma unused (context) /* for now; in future this can reference a container object */
4450 /* Check for parents in keychain cache */
4451 CFArrayRef parents
= parentCacheRead(certificate
);
4456 /* Cache miss. Query for parents. */
4458 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4460 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4461 CFRetainSafe(normalizedIssuer
);
4464 CFMutableArrayRef combinedSearchList
= NULL
;
4466 /* Define the array of keychains which will be searched for parents. */
4467 CFArrayRef searchList
= NULL
;
4468 status
= SecKeychainCopySearchList(&searchList
);
4470 combinedSearchList
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, searchList
);
4471 CFRelease(searchList
);
4473 combinedSearchList
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4475 SecKeychainRef rootStoreKeychain
= NULL
;
4476 status
= SecKeychainOpen(SYSTEM_ROOT_STORE_PATH
, &rootStoreKeychain
);
4477 if (rootStoreKeychain
) {
4478 if (combinedSearchList
) {
4479 CFArrayAppendValue(combinedSearchList
, rootStoreKeychain
);
4481 CFRelease(rootStoreKeychain
);
4484 /* Create and populate a fixed-size query dictionary. */
4485 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 5,
4486 &kCFTypeDictionaryKeyCallBacks
,
4487 &kCFTypeDictionaryValueCallBacks
);
4488 CFDictionaryAddValue(query
, kSecClass
, kSecClassCertificate
);
4489 CFDictionaryAddValue(query
, kSecReturnData
, kCFBooleanTrue
);
4490 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4491 if (combinedSearchList
) {
4492 CFDictionaryAddValue(query
, kSecMatchSearchList
, combinedSearchList
);
4493 CFRelease(combinedSearchList
);
4496 CFTypeRef results
= NULL
;
4497 if (normalizedIssuer
) {
4498 /* Look up certs whose subject is the same as this cert's issuer. */
4499 CFDictionaryAddValue(query
, kSecAttrSubject
, normalizedIssuer
);
4500 status
= SecItemCopyMatching_osx(query
, &results
);
4503 /* Cannot match anything without an issuer! */
4504 status
= errSecItemNotFound
;
4507 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4508 secitemlog(LOG_WARNING
, "SecItemCopyParentCertificates_osx: %d", (int)status
);
4512 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4513 CFTypeID resultType
= (results
) ? CFGetTypeID(results
) : 0;
4514 if (resultType
== CFArrayGetTypeID()) {
4515 CFIndex index
, count
= CFArrayGetCount((CFArrayRef
)results
);
4516 for (index
= 0; index
< count
; index
++) {
4517 CFDataRef data
= (CFDataRef
) CFArrayGetValueAtIndex((CFArrayRef
)results
, index
);
4519 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
4521 CFArrayAppendValue(result
, cert
);
4526 } else if (results
&& resultType
== CFDataGetTypeID()) {
4527 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)results
);
4529 CFArrayAppendValue(result
, cert
);
4533 CFReleaseSafe(results
);
4534 CFReleaseSafe(normalizedIssuer
);
4537 parentCacheWrite(certificate
, result
);
4542 SecCertificateRef
SecItemCopyStoredCertificate(SecCertificateRef certificate
, void *context
)
4544 #pragma unused (context) /* for now; in future this can reference a container object */
4546 /* Certificates are unique by issuer and serial number. */
4547 CFDataRef serialNumber
= SecCertificateCopySerialNumberData(certificate
, NULL
);
4549 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4551 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4552 CFRetainSafe(normalizedIssuer
);
4555 const void *keys
[] = {
4559 kSecAttrSerialNumber
,
4563 kSecClassCertificate
,
4569 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 5, NULL
, NULL
);
4570 CFTypeRef result
= NULL
;
4572 OSStatus status
= SecItemCopyMatching_osx(query
, &result
);
4573 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4574 secitemlog(LOG_WARNING
, "SecItemCopyStoredCertificate: %d", (int)status
);
4575 CFReleaseNull(result
);
4577 CFReleaseSafe(query
);
4578 CFReleaseSafe(serialNumber
);
4579 CFReleaseSafe(normalizedIssuer
);
4581 return (SecCertificateRef
)result
;
4585 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4586 * and attempts to return a sanitized copy for passing to the underlying
4587 * platform-specific implementation code.
4589 * If iOSOut is true, one or more translations may apply:
4590 * - SecKeychain refs are removed, since there aren't multiple keychains
4591 * - SecPolicy refs are removed, since they can't be externalized
4592 * - SecAccess refs are removed, and potentially translated to entitlements
4594 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4595 * parameter errors due to strict input checks in secd, which only permits
4596 * these constants for calls to SecItemCopyMatching.
4598 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4599 * This permits a query to be reused for non-synchronizable items, or to
4600 * resolve a search based on a persistent item reference for iOS.
4602 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4603 * parameter errors due to strict input checks in secd, which do not permit
4604 * these constants for calls to SecItemUpdate.
4607 SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
4608 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
)
4610 CFMutableDictionaryRef result
= CFDictionaryCreateMutableCopy(NULL
, 0, inOSXDict
);
4611 if (result
== NULL
) {
4616 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4620 /* Match constants are only supported on iOS for SecItemCopyMatching,
4621 * and will generate an error if passed to other SecItem API functions;
4622 * on OS X, they're just ignored if not applicable for the context.
4624 CFDictionaryRemoveValue(result
, kSecMatchPolicy
);
4625 CFDictionaryRemoveValue(result
, kSecMatchItemList
);
4626 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4627 CFDictionaryRemoveValue(result
, kSecMatchIssuers
);
4628 CFDictionaryRemoveValue(result
, kSecMatchEmailAddressIfPresent
);
4629 CFDictionaryRemoveValue(result
, kSecMatchSubjectContains
);
4630 CFDictionaryRemoveValue(result
, kSecMatchCaseInsensitive
);
4631 CFDictionaryRemoveValue(result
, kSecMatchTrustedOnly
);
4632 CFDictionaryRemoveValue(result
, kSecMatchValidOnDate
);
4633 CFDictionaryRemoveValue(result
, kSecMatchLimit
);
4634 CFDictionaryRemoveValue(result
, kSecMatchLimitOne
);
4635 CFDictionaryRemoveValue(result
, kSecMatchLimitAll
);
4639 /* Return constants are not supported on iOS for SecItemUpdate,
4640 * where they will generate an error; on OS X, they're just ignored
4641 * if not applicable for the context.
4643 CFDictionaryRemoveValue(result
, kSecReturnData
);
4644 CFDictionaryRemoveValue(result
, kSecReturnAttributes
);
4645 CFDictionaryRemoveValue(result
, kSecReturnRef
);
4646 CFDictionaryRemoveValue(result
, kSecReturnPersistentRef
);
4650 /* Searching on data is not supported. */
4651 CFDictionaryRemoveValue(result
, kSecValueData
);
4655 /* Searching on access lists is not supported */
4656 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4660 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4661 * cannot specify a keychain search list on iOS
4663 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4665 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4666 * cannot specify a keychain on iOS
4668 CFDictionaryRemoveValue(result
, kSecUseKeychain
);
4670 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4671 * unless kSecAttrAccessGroup has already been specified.
4673 SecAccessRef access
= (SecAccessRef
) CFDictionaryGetValue(result
, kSecAttrAccess
);
4674 CFStringRef accessGroup
= (CFStringRef
) CFDictionaryGetValue(result
, kSecAttrAccessGroup
);
4675 if (access
!= NULL
&& accessGroup
== NULL
) {
4676 /* Translate "InternetAccounts" application group to an access group */
4677 if (errSecSuccess
== SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4678 /* The caller is a valid member of the application group. */
4679 CFStringRef groupName
= CFSTR("appleaccount");
4680 CFTypeRef value
= CFDictionaryGetValue(result
, kSecAttrAuthenticationType
);
4681 if (value
&& CFEqual(value
, kSecAttrAuthenticationTypeHTMLForm
)) {
4682 groupName
= CFSTR("com.apple.cfnetwork");
4684 CFDictionarySetValue(result
, kSecAttrAccessGroup
, groupName
);
4687 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4689 /* If item is specified by direct reference, and this is an iOS search,
4690 * replace it with a persistent reference, if it was recorded inside ItemImpl.
4692 CFTypeRef directRef
= CFDictionaryGetValue(result
, kSecValueRef
);
4693 if (directRef
!= NULL
) {
4694 CFTypeID typeID
= CFGetTypeID(directRef
);
4695 if ((typeID
!= SecKeyGetTypeID() || SecKeyIsCDSAKey((SecKeyRef
)directRef
)) &&
4696 (typeID
!= SecCertificateGetTypeID() || SecCertificateIsItemImplInstance((SecCertificateRef
)directRef
)) &&
4697 (typeID
!= SecIdentityGetTypeID())) {
4698 CFDataRef persistentRef
= _SecItemGetPersistentReference(directRef
);
4699 if (persistentRef
) {
4700 CFDictionarySetValue(result
, kSecValuePersistentRef
, persistentRef
);
4701 CFDictionaryRemoveValue(result
, kSecValueRef
);
4706 /* If item is specified by persistent reference, and this is an iOS search,
4707 * remove the synchronizable attribute as it will be rejected by secd.
4709 CFTypeRef persistentRef
= CFDictionaryGetValue(result
, kSecValuePersistentRef
);
4710 if (persistentRef
) {
4711 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4714 /* Remove kSecAttrModificationDate; this should never be used as criteria
4715 * for a search, or to add/modify an item. (If we are cloning an item
4716 * and want to keep its modification date, we don't call this function.)
4717 * It turns out that some clients are using the full attributes dictionary
4718 * returned by SecItemCopyMatching as a query to find the same item later,
4719 * which won't work once the item is updated.
4721 CFDictionaryRemoveValue(result
, kSecAttrModificationDate
);
4723 /* Find all intermediate certificates in OSX keychain and append them in to the kSecMatchIssuers.
4724 * This is required because secd cannot do query in to the OSX keychain
4726 CFTypeRef matchIssuers
= CFDictionaryGetValue(result
, kSecMatchIssuers
);
4727 if (matchIssuers
&& CFGetTypeID(matchIssuers
) == CFArrayGetTypeID()) {
4728 CFArrayRef newMatchIssuers
= _CopyMatchingIssuers((CFArrayRef
)matchIssuers
);
4729 if (newMatchIssuers
) {
4730 CFDictionarySetValue(result
, kSecMatchIssuers
, newMatchIssuers
);
4731 CFRelease(newMatchIssuers
);
4736 /* iOS doesn't add the class attribute, so we must do it here. */
4738 CFDictionarySetValue(result
, kSecClass
, itemClass
);
4740 /* Remove attributes which are not part of the OS X database schema. */
4741 CFDictionaryRemoveValue(result
, kSecAttrAccessible
);
4742 CFDictionaryRemoveValue(result
, kSecAttrAccessControl
);
4743 CFDictionaryRemoveValue(result
, kSecAttrAccessGroup
);
4744 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4745 CFDictionaryRemoveValue(result
, kSecAttrTombstone
);
4748 /* This attribute is consumed by the bridge itself. */
4749 CFDictionaryRemoveValue(result
, kSecUseDataProtectionKeychain
);
4750 #pragma clang diagnostic push
4751 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4752 // Also remove deprecated symbol in case your CFDict is derpy
4753 CFDictionaryRemoveValue(result
, kSecAttrNoLegacy
);
4754 #pragma clang diagnostic pop
4762 _CopyMatchingIssuers(CFArrayRef matchIssuers
) {
4763 CFMutableArrayRef result
= NULL
;
4764 CFMutableDictionaryRef query
= NULL
;
4765 CFMutableDictionaryRef policyProperties
= NULL
;
4766 SecPolicyRef policy
= NULL
;
4767 CFTypeRef matchedCertificates
= NULL
;
4769 require_quiet(policyProperties
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), out
);
4770 CFDictionarySetValue(policyProperties
, kSecPolicyKU_KeyCertSign
, kCFBooleanTrue
);
4771 require_quiet(policy
= SecPolicyCreateWithProperties(kSecPolicyAppleX509Basic
, policyProperties
), out
);
4773 require_quiet(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), out
);
4774 CFDictionarySetValue(query
, kSecClass
, kSecClassCertificate
);
4775 CFDictionarySetValue(query
, kSecMatchIssuers
, matchIssuers
);
4776 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4777 CFDictionarySetValue(query
, kSecReturnAttributes
, kCFBooleanTrue
);
4778 CFDictionarySetValue(query
, kSecUseCertificatesWithMatchIssuers
, kCFBooleanTrue
);
4779 CFDictionarySetValue(query
, kSecMatchPolicy
, policy
);
4781 if (SecItemCopyMatching_osx(query
, &matchedCertificates
) == errSecSuccess
&& CFGetTypeID(matchedCertificates
) == CFArrayGetTypeID()) {
4782 require_quiet(result
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, (CFArrayRef
)matchedCertificates
), out
);
4783 for(CFIndex i
= 0; i
< CFArrayGetCount((CFArrayRef
)matchedCertificates
); ++i
) {
4784 CFDictionaryRef attributes
= (CFDictionaryRef
)CFArrayGetValueAtIndex((CFArrayRef
)matchedCertificates
, i
);
4785 CFTypeRef subject
= CFDictionaryGetValue(attributes
, kSecAttrSubject
);
4786 if (!CFArrayContainsValue(result
, CFRangeMake(0, CFArrayGetCount(result
)), subject
)) {
4787 CFArrayAppendValue(result
, subject
);
4793 CFReleaseSafe(query
);
4794 CFReleaseSafe(policyProperties
);
4795 CFReleaseSafe(policy
);
4796 CFReleaseSafe(matchedCertificates
);
4802 SecItemMergeResults(bool can_target_ios
, OSStatus status_ios
, CFTypeRef result_ios
,
4803 bool can_target_osx
, OSStatus status_osx
, CFTypeRef result_osx
,
4804 CFTypeRef
*result
) {
4805 // When querying both keychains and iOS keychain fails because of missing
4806 // entitlements, completely ignore iOS keychain result. This is to keep
4807 // backward compatibility with applications which know nothing about iOS keychain
4808 // and use SecItem API to access OSX keychain which does not need any entitlements.
4809 if (can_target_osx
&& can_target_ios
&& status_ios
== errSecMissingEntitlement
) {
4810 can_target_ios
= false;
4813 if (can_target_osx
&& can_target_ios
) {
4814 // If both keychains were targetted, examine returning statuses and decide what to do.
4815 if (status_ios
!= errSecSuccess
) {
4816 // iOS keychain failed to produce results because of some error, go with results from OSX keychain.
4817 // Since iOS keychain queries will fail without a keychain-access-group or proper entitlements, SecItemCopyMatching
4818 // calls against the OSX keychain API that should return errSecItemNotFound will return nonsense from the iOS keychain.
4819 AssignOrReleaseResult(result_osx
, result
);
4821 } else if (status_osx
!= errSecSuccess
) {
4822 if (status_osx
!= errSecItemNotFound
) {
4823 // OSX failed to produce results with some failure mode (else than not_found), but iOS produced results.
4824 // We have to either return OSX failure result and discard iOS results, or vice versa. For now, we just
4825 // ignore OSX error and return just iOS results.
4826 secitemlog(LOG_NOTICE
, "SecItemMergeResults: osx_result=%d, ignoring it, iOS succeeded fine", status_osx
);
4829 // OSX failed to produce results, but we have success from iOS keychain; go with results from iOS keychain.
4830 AssignOrReleaseResult(result_ios
, result
);
4831 return errSecSuccess
;
4833 // Both searches succeeded, merge results.
4834 if (result
!= NULL
) {
4835 CFTypeID id_osx
= (result_osx
) ? CFGetTypeID(result_osx
) : 0;
4836 CFTypeID id_ios
= (result_ios
) ? CFGetTypeID(result_ios
) : 0;
4837 CFTypeID id_array
= CFArrayGetTypeID();
4838 if ((id_osx
== id_array
) && (id_ios
== id_array
)) {
4839 // Fold the arrays into one.
4840 *result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4841 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_ios
,
4842 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_ios
)));
4843 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_osx
,
4844 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_osx
)));
4846 // Result type is not an array, so only one match can be returned.
4847 *result
= (id_ios
) ? result_ios
: result_osx
;
4848 CFRetainSafe(*result
);
4851 CFReleaseSafe(result_osx
);
4852 CFReleaseSafe(result_ios
);
4853 return errSecSuccess
;
4855 } else if (can_target_ios
) {
4856 // Only iOS keychain was targetted.
4857 AssignOrReleaseResult(result_ios
, result
);
4859 } else if (can_target_osx
) {
4860 // Only OSX keychain was targetted.
4861 AssignOrReleaseResult(result_osx
, result
);
4864 // Query could not run at all?
4870 SecItemCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
)
4872 os_activity_t activity
= os_activity_create("SecItemCopyMatching", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4873 os_activity_scope(activity
);
4874 os_release(activity
);
4879 secitemshow(query
, "SecItemCopyMatching query:");
4881 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4882 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4883 bool can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
;
4884 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
);
4885 if (status
!= errSecSuccess
) {
4889 if (can_target_ios
) {
4890 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4891 CFDictionaryGetValue(query
, kSecClass
), true, false, false, false, true, true);
4893 status_ios
= errSecParam
;
4896 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4897 CFRelease(attrs_ios
);
4899 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_ios result: %d", status_ios
);
4902 if (can_target_osx
) {
4903 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4904 CFDictionaryGetValue(query
, kSecClass
), false, false, true, false, true, true);
4906 status_osx
= errSecParam
;
4909 status_osx
= SecItemCopyMatching_osx(attrs_osx
, &result_osx
);
4910 CFRelease(attrs_osx
);
4912 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_osx result: %d", status_osx
);
4915 status
= SecItemMergeResults(can_target_ios
, status_ios
, result_ios
,
4916 can_target_osx
, status_osx
, result_osx
, result
);
4917 secitemlog(LOG_NOTICE
, "SecItemCopyMatching result: %d", status
);
4922 SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
)
4924 os_activity_t activity
= os_activity_create("SecItemAdd", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4925 os_activity_scope(activity
);
4926 os_release(activity
);
4934 secitemshow(attributes
, "SecItemAdd attrs:");
4936 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4937 bool can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
;
4938 OSStatus status
= SecItemCategorizeQuery(attributes
, can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
);
4939 if (status
!= errSecSuccess
) {
4943 // SecItemAdd cannot be really done on both keychains. In order to keep backward compatibility
4944 // with existing applications, we prefer to add items into legacy keychain and fallback
4945 // into iOS (modern) keychain only when the query is not suitable for legacy keychain.
4946 if (!can_target_osx
) {
4947 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(attributes
,
4948 NULL
, true, true, false, false, false, false);
4950 status
= errSecParam
;
4952 status
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4953 CFRelease(attrs_ios
);
4955 secitemlog(LOG_NOTICE
, "SecItemAdd_ios result: %d", status
);
4956 AssignOrReleaseResult(result_ios
, result
);
4959 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(attributes
,
4960 NULL
, false, false, true, false, false, false);
4962 status
= errSecParam
;
4964 status
= SecItemAdd_osx(attrs_osx
, &result_osx
);
4965 CFRelease(attrs_osx
);
4967 secitemlog(LOG_NOTICE
, "SecItemAdd_osx result: %d", status
);
4968 AssignOrReleaseResult(result_osx
, result
);
4974 SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
)
4976 os_activity_t activity
= os_activity_create("SecItemUpdate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4977 os_activity_scope(activity
);
4978 os_release(activity
);
4980 if (!query
|| !attributesToUpdate
) {
4983 secitemshow(query
, "SecItemUpdate query:");
4984 secitemshow(attributesToUpdate
, "SecItemUpdate attrs:");
4986 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4987 bool can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
;
4988 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
);
4989 if (status
!= errSecSuccess
) {
4994 * If the user have explicity opted in to UseDataProtectionKeychain, then don't touch the legacy keychain at all
4995 * ie don't move the item back to legacy keychain if you remove sync=1 or the inverse.
4997 if (useDataProtectionKeychainFlag
) {
4998 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4999 CFDictionaryGetValue(query
, kSecClass
), true, true, false, true, true, true);
5001 status_ios
= errSecParam
;
5003 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
5004 CFRelease(attrs_ios
);
5006 secitemlog(LOG_NOTICE
, "SecItemUpdate(ios only) result: %d", status_ios
);
5010 if (can_target_ios
) {
5011 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
5012 CFDictionaryGetValue(query
, kSecClass
), true, true, false, true, true, true);
5014 status_ios
= errSecParam
;
5017 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate
)) {
5018 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
5020 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
5022 CFRelease(attrs_ios
);
5024 secitemlog(LOG_NOTICE
, "SecItemUpdate_ios result: %d", status_ios
);
5027 if (can_target_osx
) {
5028 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
5029 CFDictionaryGetValue(query
, kSecClass
), false, false, true, true, true, true);
5031 status_osx
= errSecParam
;
5034 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate
))
5035 status_osx
= SecItemChangeSynchronizability(attrs_osx
, attributesToUpdate
, true);
5037 status_osx
= SecItemUpdate_osx(attrs_osx
, attributesToUpdate
);
5039 CFRelease(attrs_osx
);
5041 secitemlog(LOG_NOTICE
, "SecItemUpdate_osx result: %d", status_osx
);
5044 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
5045 can_target_osx
, status_osx
, NULL
, NULL
);
5046 secitemlog(LOG_NOTICE
, "SecItemUpdate result: %d", status
);
5051 SecItemDelete(CFDictionaryRef query
)
5053 os_activity_t activity
= os_activity_create("SecItemDelete", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
5054 os_activity_scope(activity
);
5055 os_release(activity
);
5060 secitemshow(query
, "SecItemDelete query:");
5062 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
5063 bool can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
;
5064 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
, useDataProtectionKeychainFlag
);
5065 if (status
!= errSecSuccess
) {
5069 if (can_target_ios
) {
5070 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
5071 NULL
, true, true, false, true, true, true);
5073 status_ios
= errSecParam
;
5075 status_ios
= SecItemDelete_ios(attrs_ios
);
5076 CFRelease(attrs_ios
);
5078 secitemlog(LOG_NOTICE
, "SecItemDelete_ios result: %d", status_ios
);
5081 if (can_target_osx
) {
5082 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
5083 NULL
, false, false, true, true, true, true);
5085 status_osx
= errSecParam
;
5087 status_osx
= SecItemDelete_osx(attrs_osx
);
5088 CFRelease(attrs_osx
);
5090 secitemlog(LOG_NOTICE
, "SecItemDelete_osx result: %d", status_osx
);
5093 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
5094 can_target_osx
, status_osx
, NULL
, NULL
);
5095 secitemlog(LOG_NOTICE
, "SecItemCopyDelete result: %d", status
);
5100 SecItemCopyMatching_osx(
5101 CFDictionaryRef query
,
5104 if (!query
|| !result
)
5109 setCountLegacyAPIEnabledForThread(false);
5111 CFAllocatorRef allocator
= CFGetAllocator(query
);
5112 CFIndex matchCount
= 0;
5113 CFMutableArrayRef itemArray
= NULL
;
5114 SecKeychainItemRef item
= NULL
;
5115 SecIdentityRef identity
= NULL
;
5116 OSStatus tmpStatus
, status
= errSecSuccess
;
5118 // validate input query parameters and create the search reference
5119 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(query
, &status
);
5120 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
5122 // find the next match until we hit maxMatches, or no more matches found
5123 while ( !(!itemParams
->returnAllMatches
&& matchCount
>= itemParams
->maxMatches
) &&
5124 SecItemSearchCopyNext(itemParams
, (CFTypeRef
*)&item
) == errSecSuccess
) {
5126 if (FilterCandidateItem((CFTypeRef
*)&item
, itemParams
, &identity
))
5127 continue; // move on to next item
5129 ++matchCount
; // we have a match
5131 tmpStatus
= AddItemResults(item
, identity
, itemParams
, allocator
, &itemArray
, result
);
5132 if (tmpStatus
&& (status
== errSecSuccess
))
5140 CFRelease(identity
);
5145 if (status
== errSecSuccess
)
5146 status
= (matchCount
> 0) ? errSecSuccess
: errSecItemNotFound
;
5149 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5153 _FreeSecItemParams(itemParams
);
5155 setCountLegacyAPIEnabledForThread(true);
5162 CFDictionaryRef attributes
,
5170 setCountLegacyAPIEnabledForThread(false);
5172 CFAllocatorRef allocator
= CFGetAllocator(attributes
);
5173 CFMutableArrayRef itemArray
= NULL
;
5174 SecKeychainItemRef item
= NULL
;
5175 OSStatus tmpStatus
, status
= errSecSuccess
;
5177 // validate input attribute parameters
5178 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(attributes
, &status
);
5179 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
5181 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
5182 // since the private key should already be in a keychain by definition. We could support
5183 // this as a copy operation for the private key if a different keychain is specified,
5184 // but in any case it should try to add the certificate. See <rdar://8317887>.
5185 require_action(!itemParams
->returnIdentity
, error_exit
, status
= errSecItemInvalidValue
);
5187 if (itemParams
->useItems
== NULL
) {
5189 require_action(itemParams
->itemData
== NULL
|| CFGetTypeID(itemParams
->itemData
) == CFDataGetTypeID(),
5190 error_exit
, status
= errSecItemInvalidValue
);
5192 // create a single keychain item specified by the input attributes
5193 status
= SecKeychainItemCreateFromContent(itemParams
->itemClass
,
5194 itemParams
->attrList
,
5195 (itemParams
->itemData
) ? (UInt32
)CFDataGetLength(itemParams
->itemData
) : 0,
5196 (itemParams
->itemData
) ? CFDataGetBytePtrVoid(itemParams
->itemData
) : NULL
,
5197 itemParams
->keychain
,
5200 require_noerr(status
, error_exit
);
5202 // return results (if requested)
5204 itemParams
->maxMatches
= 1; // in case kSecMatchLimit was set to > 1
5205 tmpStatus
= AddItemResults(item
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5206 if (tmpStatus
&& (status
== errSecSuccess
))
5212 // add multiple items which are specified in the itemParams->useItems array.
5213 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
5214 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
5215 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
5217 OSStatus aggregateStatus
= errSecSuccess
;
5218 CFIndex ix
, count
= CFArrayGetCount(itemParams
->useItems
);
5219 itemParams
->maxMatches
= (count
> 1) ? (int)count
: 2; // force results to always be returned as an array
5220 for (ix
=0; ix
< count
; ix
++) {
5221 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->useItems
, ix
);
5223 if (SecCertificateGetTypeID() == CFGetTypeID(anItem
)) {
5224 // SecCertificateRef item
5225 tmpStatus
= SecCertificateAddToKeychain((SecCertificateRef
)anItem
, itemParams
->keychain
);
5226 if (!tmpStatus
&& result
) {
5227 tmpStatus
= AddItemResults((SecKeychainItemRef
)anItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5229 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5231 else if (SecKeyGetTypeID() == CFGetTypeID(anItem
)) {
5233 SecKeychainRef itemKeychain
= NULL
;
5234 tmpStatus
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)anItem
, &itemKeychain
);
5235 if (tmpStatus
== errSecSuccess
) {
5236 // key was in a keychain, so we can attempt to copy it
5237 SecKeychainItemRef itemCopy
= NULL
;
5238 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5239 if (!tmpStatus
&& result
) {
5240 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5243 CFRelease(itemCopy
);
5247 // key was not in any keychain, so must be imported
5248 SecKeychainItemRef keyItem
= NULL
;
5249 tmpStatus
= _ImportKey((SecKeyRef
)anItem
, itemParams
->keychain
, itemParams
->access
, itemParams
->attrList
, &keyItem
);
5250 if (!tmpStatus
&& result
) {
5251 tmpStatus
= AddItemResults(keyItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5258 CFRelease(itemKeychain
);
5260 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5262 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem
)) {
5263 // SecKeychainItemRef item
5264 SecKeychainItemRef itemCopy
= NULL
;
5265 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5266 if (!tmpStatus
&& result
) {
5267 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5270 CFRelease(itemCopy
);
5272 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5274 else if (CFDataGetTypeID() == CFGetTypeID(anItem
)) {
5275 // CFDataRef item (persistent reference)
5276 SecKeychainItemRef realItem
= NULL
;
5277 tmpStatus
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
);
5278 if (tmpStatus
== errSecSuccess
) {
5279 // persistent reference resolved to a keychain item, so we can attempt to copy it
5280 SecKeychainItemRef itemCopy
= NULL
;
5281 tmpStatus
= SecKeychainItemCreateCopy(realItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5282 if (!tmpStatus
&& result
) {
5283 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5286 CFRelease(itemCopy
);
5290 CFRelease(realItem
);
5292 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5295 } // end of itemList array loop
5296 status
= aggregateStatus
;
5297 } // end processing multiple items
5300 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5304 _FreeSecItemParams(itemParams
);
5305 setCountLegacyAPIEnabledForThread(true);
5312 CFDictionaryRef query
,
5313 CFDictionaryRef attributesToUpdate
)
5315 if (!query
|| !attributesToUpdate
)
5318 // run the provided query to get a list of items to update
5319 CFTypeRef results
= NULL
;
5320 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5321 if (status
!= errSecSuccess
)
5322 return status
; // nothing was matched, or the query was bad
5324 CFArrayRef items
= NULL
;
5325 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5326 items
= (CFArrayRef
) results
;
5329 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5333 setCountLegacyAPIEnabledForThread(false);
5335 OSStatus result
= errSecSuccess
;
5336 CFIndex ix
, count
= CFArrayGetCount(items
);
5337 for (ix
=0; ix
< count
; ix
++) {
5338 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5340 status
= _UpdateKeychainItem(anItem
, attributesToUpdate
);
5341 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);
5345 setCountLegacyAPIEnabledForThread(true);
5355 CFDictionaryRef query
)
5360 // run the provided query to get a list of items to delete
5361 CFTypeRef results
= NULL
;
5362 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5363 if (status
!= errSecSuccess
)
5364 return status
; // nothing was matched, or the query was bad
5366 CFArrayRef items
= NULL
;
5367 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5368 items
= (CFArrayRef
) results
;
5371 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5375 setCountLegacyAPIEnabledForThread(false);
5377 OSStatus result
= errSecSuccess
;
5378 CFIndex ix
, count
= CFArrayGetCount(items
);
5379 for (ix
=0; ix
< count
; ix
++) {
5380 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5382 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
)) {
5383 status
= _DeleteIdentity((SecIdentityRef
)anItem
);
5386 status
= _DeleteKeychainItem(anItem
);
5388 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);
5392 setCountLegacyAPIEnabledForThread(true);