2 * Copyright (c) 2006-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecBridge.h"
25 #include "SecInternal.h"
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <security_utilities/cfutilities.h>
28 #include <Security/SecBase.h>
29 #include <Security/SecKeychainItem.h>
30 #include <Security/SecCertificate.h>
31 #include <Security/SecCertificatePriv.h>
32 #include <sys/param.h>
33 #include "cssmdatetime.h"
35 #include "SecItemPriv.h"
36 #include "SecIdentitySearchPriv.h"
37 #include "SecKeychainPriv.h"
38 #include "SecCertificatePriv.h"
39 #include "SecCertificatePrivP.h"
40 #include "TrustAdditions.h"
41 #include "TrustSettingsSchema.h"
42 #include <Security/SecTrustPriv.h>
43 #include "utilities/array_size.h"
45 #include <AssertMacros.h>
49 #include <Security/SecTrustedApplication.h>
50 #include <Security/SecTrustedApplicationPriv.h>
51 #include <Security/SecCode.h>
52 #include <Security/SecCodePriv.h>
53 #include <Security/SecRequirement.h>
55 #include <login/SessionAgentCom.h>
56 #include <login/SessionAgentStatusCom.h>
57 #include <os/activity.h>
60 const uint8_t kUUIDStringLength
= 36;
62 OSStatus
SecItemAdd_osx(CFDictionaryRef attributes
, CFTypeRef
*result
);
63 OSStatus
SecItemCopyMatching_osx(CFDictionaryRef query
, CFTypeRef
*result
);
64 OSStatus
SecItemUpdate_osx(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
65 OSStatus
SecItemDelete_osx(CFDictionaryRef query
);
68 OSStatus
SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
);
69 OSStatus
SecItemCopyMatching_ios(CFDictionaryRef query
, CFTypeRef
*result
);
70 OSStatus
SecItemUpdate_ios(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
71 OSStatus
SecItemDelete_ios(CFDictionaryRef query
);
72 OSStatus
SecItemUpdateTokenItems_ios(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
);
75 OSStatus
SecItemValidateAppleApplicationGroupAccess(CFStringRef group
);
76 CFDictionaryRef
SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
77 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
);
79 bool _SecItemParsePersistentRef(CFDataRef persistent_ref
, CFStringRef
*return_class
,
80 long long int *return_rowid
, CFDictionaryRef
*return_token_attrs
);
83 static Boolean
SecItemSynchronizable(CFDictionaryRef query
);
85 static void secitemlog(int priority
, const char *format
, ...)
90 if (priority
< LOG_NOTICE
) // log warnings and errors
94 va_start(list
, format
);
95 vsyslog(priority
, format
, list
);
100 static void secitemshow(CFTypeRef obj
, const char *context
)
103 CFStringRef desc
= CFCopyDescription(obj
);
106 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc
), kCFStringEncodingUTF8
) + 1;
107 char* buffer
= (char*) malloc(length
);
109 Boolean converted
= CFStringGetCString(desc
, buffer
, length
, kCFStringEncodingUTF8
);
111 const char *prefix
= (context
) ? context
: "";
112 const char *separator
= (context
) ? " " : "";
113 secitemlog(LOG_NOTICE
, "%s%s%s", prefix
, separator
, buffer
);
122 #define CFDataGetBytePtrVoid CFDataGetBytePtr
124 #pragma mark SecItem private utility functions
126 /******************************************************************************/
128 struct ProtocolAttributeInfo
{
129 const CFStringRef
*protocolValue
;
130 SecProtocolType protocolType
;
133 static ProtocolAttributeInfo gProtocolTypes
[] = {
134 { &kSecAttrProtocolFTP
, kSecProtocolTypeFTP
},
135 { &kSecAttrProtocolFTPAccount
, kSecProtocolTypeFTPAccount
},
136 { &kSecAttrProtocolHTTP
, kSecProtocolTypeHTTP
},
137 { &kSecAttrProtocolIRC
, kSecProtocolTypeIRC
},
138 { &kSecAttrProtocolNNTP
, kSecProtocolTypeNNTP
},
139 { &kSecAttrProtocolPOP3
, kSecProtocolTypePOP3
},
140 { &kSecAttrProtocolSMTP
, kSecProtocolTypeSMTP
},
141 { &kSecAttrProtocolSOCKS
, kSecProtocolTypeSOCKS
},
142 { &kSecAttrProtocolIMAP
, kSecProtocolTypeIMAP
},
143 { &kSecAttrProtocolLDAP
, kSecProtocolTypeLDAP
},
144 { &kSecAttrProtocolAppleTalk
, kSecProtocolTypeAppleTalk
},
145 { &kSecAttrProtocolAFP
, kSecProtocolTypeAFP
},
146 { &kSecAttrProtocolTelnet
, kSecProtocolTypeTelnet
},
147 { &kSecAttrProtocolSSH
, kSecProtocolTypeSSH
},
148 { &kSecAttrProtocolFTPS
, kSecProtocolTypeFTPS
},
149 { &kSecAttrProtocolHTTPS
, kSecProtocolTypeHTTPS
},
150 { &kSecAttrProtocolHTTPProxy
, kSecProtocolTypeHTTPProxy
},
151 { &kSecAttrProtocolHTTPSProxy
, kSecProtocolTypeHTTPSProxy
},
152 { &kSecAttrProtocolFTPProxy
, kSecProtocolTypeFTPProxy
},
153 { &kSecAttrProtocolSMB
, kSecProtocolTypeSMB
},
154 { &kSecAttrProtocolRTSP
, kSecProtocolTypeRTSP
},
155 { &kSecAttrProtocolRTSPProxy
, kSecProtocolTypeRTSPProxy
},
156 { &kSecAttrProtocolDAAP
, kSecProtocolTypeDAAP
},
157 { &kSecAttrProtocolEPPC
, kSecProtocolTypeEPPC
},
158 { &kSecAttrProtocolIPP
, kSecProtocolTypeIPP
},
159 { &kSecAttrProtocolNNTPS
, kSecProtocolTypeNNTPS
},
160 { &kSecAttrProtocolLDAPS
, kSecProtocolTypeLDAPS
},
161 { &kSecAttrProtocolTelnetS
, kSecProtocolTypeTelnetS
},
162 { &kSecAttrProtocolIMAPS
, kSecProtocolTypeIMAPS
},
163 { &kSecAttrProtocolIRCS
, kSecProtocolTypeIRCS
},
164 { &kSecAttrProtocolPOP3S
, kSecProtocolTypePOP3S
}
167 static const int kNumberOfProtocolTypes
= sizeof(gProtocolTypes
) / sizeof(ProtocolAttributeInfo
);
170 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
172 static SecProtocolType
173 _SecProtocolTypeForSecAttrProtocol(
176 SecProtocolType result
= kSecProtocolTypeAny
;
178 if (protocol
!= NULL
) {
180 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
181 if (CFEqual(protocol
, *(gProtocolTypes
[count
].protocolValue
))) {
182 result
= gProtocolTypes
[count
].protocolType
;
192 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
195 _SecAttrProtocolForSecProtocolType(
196 SecProtocolType protocolType
)
198 CFTypeRef result
= NULL
;
200 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
201 if (gProtocolTypes
[count
].protocolType
== protocolType
) {
202 result
= *(gProtocolTypes
[count
].protocolValue
);
211 /******************************************************************************/
213 struct AuthenticationAttributeInfo
{
214 const CFStringRef
*authValue
;
215 SecAuthenticationType authType
;
218 static AuthenticationAttributeInfo gAuthTypes
[] = {
219 { &kSecAttrAuthenticationTypeNTLM
, kSecAuthenticationTypeNTLM
},
220 { &kSecAttrAuthenticationTypeMSN
, kSecAuthenticationTypeMSN
},
221 { &kSecAttrAuthenticationTypeDPA
, kSecAuthenticationTypeDPA
},
222 { &kSecAttrAuthenticationTypeRPA
, kSecAuthenticationTypeRPA
},
223 { &kSecAttrAuthenticationTypeHTTPBasic
, kSecAuthenticationTypeHTTPBasic
},
224 { &kSecAttrAuthenticationTypeHTTPDigest
, kSecAuthenticationTypeHTTPDigest
},
225 { &kSecAttrAuthenticationTypeHTMLForm
, kSecAuthenticationTypeHTMLForm
},
226 { &kSecAttrAuthenticationTypeDefault
, kSecAuthenticationTypeDefault
}
229 static const int kNumberOfAuthenticationTypes
= sizeof(gAuthTypes
) / sizeof(AuthenticationAttributeInfo
);
232 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
233 * SecAttrAuthenticationType to a SecAuthenticationType.
235 static SecAuthenticationType
236 _SecAuthenticationTypeForSecAttrAuthenticationType(
237 CFTypeRef authenticationType
)
239 SecAuthenticationType result
= kSecAuthenticationTypeAny
;
241 if (authenticationType
!= NULL
) {
243 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
244 if (CFEqual(authenticationType
, *(gAuthTypes
[count
].authValue
))) {
245 result
= gAuthTypes
[count
].authType
;
255 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
256 * to a SecAttrAuthenticationType.
259 _SecAttrAuthenticationTypeForSecAuthenticationType(
260 SecAuthenticationType authenticationType
)
262 CFTypeRef result
= NULL
;
264 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
265 if (gAuthTypes
[count
].authType
== authenticationType
) {
266 result
= *(gAuthTypes
[count
].authValue
);
275 /******************************************************************************/
277 struct KeyAlgorithmInfo
{
278 const CFStringRef
*keyType
;
282 static KeyAlgorithmInfo gKeyTypes
[] = {
283 { &kSecAttrKeyTypeRSA
, CSSM_ALGID_RSA
},
284 { &kSecAttrKeyTypeDSA
, CSSM_ALGID_DSA
},
285 { &kSecAttrKeyTypeAES
, CSSM_ALGID_AES
},
286 { &kSecAttrKeyTypeDES
, CSSM_ALGID_DES
},
287 { &kSecAttrKeyType3DES
, CSSM_ALGID_3DES
},
288 { &kSecAttrKeyTypeRC4
, CSSM_ALGID_RC4
},
289 { &kSecAttrKeyTypeRC2
, CSSM_ALGID_RC2
},
290 { &kSecAttrKeyTypeCAST
, CSSM_ALGID_CAST
},
291 { &kSecAttrKeyTypeECDSA
, CSSM_ALGID_ECDSA
},
292 { &kSecAttrKeyTypeEC
, CSSM_ALGID_ECDSA
}
295 static const int kNumberOfKeyTypes
= sizeof(gKeyTypes
) / sizeof (KeyAlgorithmInfo
);
298 static UInt32
_SecAlgorithmTypeFromSecAttrKeyType(
299 CFTypeRef keyTypeRef
)
301 UInt32 keyAlgValue
= 0;
302 if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef
))
306 for (ix
=0; ix
<kNumberOfKeyTypes
; ix
++) {
307 if (CFEqual(keyTypeRef
, *(gKeyTypes
[ix
].keyType
))) {
308 keyAlgValue
= gKeyTypes
[ix
].keyValue
;
313 //%%%TODO try to convert the input string to a number here
319 enum ItemRepresentation
321 kStringRepresentation
,
323 kNumberRepresentation
,
324 kBooleanRepresentation
,
329 struct InternalAttributeListInfo
332 const CFStringRef
*newItemType
;
333 ItemRepresentation itemRepresentation
;
337 static InternalAttributeListInfo gGenericPasswordAttributes
[] =
339 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
340 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
341 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
342 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
343 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
344 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
345 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
346 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
347 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
348 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
349 { kSecServiceItemAttr
, &kSecAttrService
, kStringRepresentation
},
350 { kSecGenericItemAttr
, &kSecAttrGeneric
, kDataRepresentation
}
353 static const int kNumberOfGenericPasswordAttributes
= sizeof(gGenericPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
356 static InternalAttributeListInfo gInternetPasswordAttributes
[] =
358 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
359 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
360 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
361 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
362 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
363 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
364 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
365 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
366 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
367 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
368 { kSecSecurityDomainItemAttr
, &kSecAttrSecurityDomain
, kStringRepresentation
},
369 { kSecServerItemAttr
, &kSecAttrServer
, kStringRepresentation
},
370 { kSecAuthenticationTypeItemAttr
, &kSecAttrAuthenticationType
, kStringRepresentation
}, // maps from UInt32 value to string constant
371 { kSecPortItemAttr
, &kSecAttrPort
, kNumberRepresentation
},
372 { kSecPathItemAttr
, &kSecAttrPath
, kStringRepresentation
}
375 static const int kNumberOfInternetPasswordAttributes
= sizeof(gInternetPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
378 static InternalAttributeListInfo gCertificateAttributes
[] =
380 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
381 { kSecSubjectItemAttr
, &kSecAttrSubject
, kDataRepresentation
},
382 { kSecIssuerItemAttr
, &kSecAttrIssuer
, kDataRepresentation
},
383 { kSecSerialNumberItemAttr
, &kSecAttrSerialNumber
, kDataRepresentation
},
384 { kSecPublicKeyHashItemAttr
, &kSecAttrPublicKeyHash
, kDataRepresentation
},
385 { kSecSubjectKeyIdentifierItemAttr
, &kSecAttrSubjectKeyID
, kDataRepresentation
},
386 { kSecCertTypeItemAttr
, &kSecAttrCertificateType
, kDataRepresentation
},
387 { kSecCertEncodingItemAttr
, &kSecAttrCertificateEncoding
, kDataRepresentation
}
390 static const int kNumberOfCertificateAttributes
= sizeof(gCertificateAttributes
) / sizeof(InternalAttributeListInfo
);
393 static InternalAttributeListInfo gKeyAttributes
[] =
395 { kSecKeyKeyClass
, &kSecAttrKeyClass
, kStringRepresentation
}, // key class maps from UInt32 value to string constant
396 { kSecKeyPrintName
, &kSecAttrLabel
, kStringRepresentation
}, // note that "print name" maps to the user-visible label
397 // { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
398 { kSecKeyPermanent
, &kSecAttrIsPermanent
, kBooleanRepresentation
},
399 // { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
400 // { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
401 { 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
402 { kSecKeyApplicationTag
, &kSecAttrApplicationTag
, kDataRepresentation
},
403 // { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
404 { kSecKeyKeyType
, &kSecAttrKeyType
, kStringRepresentation
}, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
405 { kSecKeyKeySizeInBits
, &kSecAttrKeySizeInBits
, kNumberRepresentation
},
406 { kSecKeyEffectiveKeySize
, &kSecAttrEffectiveKeySize
, kNumberRepresentation
},
407 // { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
408 // { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
409 // { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
410 // { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
411 // { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
412 // { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
413 { kSecKeyEncrypt
, &kSecAttrCanEncrypt
, kBooleanRepresentation
},
414 { kSecKeyDecrypt
, &kSecAttrCanDecrypt
, kBooleanRepresentation
},
415 { kSecKeyDerive
, &kSecAttrCanDerive
, kBooleanRepresentation
},
416 { kSecKeySign
, &kSecAttrCanSign
, kBooleanRepresentation
},
417 { kSecKeyVerify
, &kSecAttrCanVerify
, kBooleanRepresentation
},
418 // { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
419 // { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
420 { kSecKeyWrap
, &kSecAttrCanWrap
, kBooleanRepresentation
},
421 { kSecKeyUnwrap
, &kSecAttrCanUnwrap
, kBooleanRepresentation
}
424 static const int kNumberOfKeyAttributes
= sizeof(gKeyAttributes
) / sizeof(InternalAttributeListInfo
);
427 static void* CloneDataByType(ItemRepresentation type
, CFTypeRef value
, UInt32
& length
)
431 case kStringRepresentation
:
433 if (CFStringGetTypeID() != CFGetTypeID(value
)) {
437 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
438 char* buffer
= (char*) malloc(maxLength
);
439 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
441 length
= (UInt32
)strlen(buffer
);
451 case kDataRepresentation
:
453 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
454 // We may have a string here, since the key label may be a GUID for the symmetric keys
455 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
456 char* buffer
= (char*) malloc(maxLength
);
457 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
459 length
= (UInt32
)strlen(buffer
);
469 if (CFDataGetTypeID() != CFGetTypeID(value
)) {
473 length
= (UInt32
)CFDataGetLength((CFDataRef
) value
);
474 uint8_t* buffer
= (uint8_t*) malloc(length
);
475 CFDataGetBytes((CFDataRef
) value
, CFRangeMake(0, length
), buffer
);
479 case kNumberRepresentation
:
481 if (CFNumberGetTypeID() != CFGetTypeID(value
)) {
485 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
486 Boolean converted
= CFNumberGetValue((CFNumberRef
) value
, kCFNumberSInt32Type
, buffer
);
488 length
= sizeof(uint32_t);
498 case kBooleanRepresentation
:
500 if (CFBooleanGetTypeID() != CFGetTypeID(value
)) {
504 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
505 *buffer
= (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
506 length
= sizeof(uint32_t);
510 case kDateRepresentation
:
512 if (CFDateGetTypeID() != CFGetTypeID(value
)) {
516 char* buffer
= (char*) calloc(1, 32); // max length of a CSSM date string
517 CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef
) value
, buffer
);
518 length
= (UInt32
)strlen(buffer
);
532 _ConvertNewFormatToOldFormat(
533 CFAllocatorRef allocator
,
534 const InternalAttributeListInfo
* info
,
536 CFDictionaryRef dictionaryRef
,
537 SecKeychainAttributeList
* &attrList
540 // get the keychain attributes array from the data item
541 // here's the problem. On the one hand, we have a dictionary that is purported to contain
542 // attributes for our type. On the other hand, the dictionary may contain items we don't support,
543 // and we therefore don't know how many attributes we will have unless we count them first
546 attrList
= (SecKeychainAttributeList
*) calloc(1, sizeof(SecKeychainAttributeList
));
548 // make storage to extract the dictionary items
549 CFIndex itemsInDictionary
= CFDictionaryGetCount(dictionaryRef
);
550 CFTypeRef keys
[itemsInDictionary
];
551 CFTypeRef values
[itemsInDictionary
];
553 CFTypeRef
*keysPtr
= keys
;
554 CFTypeRef
*valuesPtr
= values
;
556 CFDictionaryGetKeysAndValues(dictionaryRef
, keys
, values
);
558 // count the number of items we are interested in
562 // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
563 // we don't pay the price for this twice
564 SecKeychainAttrType tags
[itemsInDictionary
];
565 ItemRepresentation types
[itemsInDictionary
];
567 for (i
= 0; i
< itemsInDictionary
; ++i
)
569 CFTypeRef key
= keysPtr
[i
];
572 for (j
= 0; j
< infoNumItems
; ++j
)
574 if (CFEqual(*(info
[j
].newItemType
), key
))
576 tags
[i
] = info
[j
].oldItemType
;
577 types
[i
] = info
[j
].itemRepresentation
;
583 if (j
>= infoNumItems
)
585 // if we got here, we aren't interested in this item.
590 // now we can make the result array
591 attrList
->count
= (UInt32
)count
;
592 attrList
->attr
= (count
> 0) ? (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * count
) : NULL
;
594 // fill out the array
595 int resultPointer
= 0;
596 for (i
= 0; i
< itemsInDictionary
; ++i
)
598 if (values
[i
] != NULL
)
600 attrList
->attr
[resultPointer
].tag
= tags
[i
];
602 // we have to clone the data pointer. The caller will need to make sure to throw these away
603 // with _FreeAttrList when it is done...
604 attrList
->attr
[resultPointer
].data
= CloneDataByType(types
[i
], valuesPtr
[i
], attrList
->attr
[resultPointer
].length
);
609 return errSecSuccess
;
615 _ConvertOldFormatToNewFormat(
616 CFAllocatorRef allocator
,
617 const InternalAttributeListInfo
* info
,
619 SecKeychainItemRef itemRef
,
620 CFMutableDictionaryRef
& dictionaryRef
)
622 SecKeychainAttributeList list
;
623 list
.count
= infoNumItems
;
624 list
.attr
= (SecKeychainAttribute
*) calloc(infoNumItems
, sizeof(SecKeychainAttribute
));
626 // fill out the array. We only need to fill in the tags, since calloc zeros what it returns
628 for (i
= 0; i
< infoNumItems
; ++i
)
630 list
.attr
[i
].tag
= info
[i
].oldItemType
;
633 OSStatus result
= SecKeychainItemCopyContent(itemRef
, NULL
, &list
, NULL
, NULL
);
634 if (result
!= errSecSuccess
)
636 dictionaryRef
= NULL
;
641 // create the dictionary
642 dictionaryRef
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
645 for (i
= 0; i
< infoNumItems
; ++i
)
647 if (list
.attr
[i
].data
== NULL
)
650 switch (info
[i
].itemRepresentation
)
652 case kStringRepresentation
:
654 CFStringRef stringRef
;
655 if (info
[i
].oldItemType
== kSecKeyKeyClass
) {
656 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
657 uint32_t keyRecordValue
= *((uint32_t*)list
.attr
[i
].data
);
658 bool retainString
= true;
659 switch (keyRecordValue
) {
660 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
661 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
663 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
664 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
666 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
667 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
670 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyRecordValue
);
671 retainString
= false;
675 if (retainString
) CFRetain(stringRef
);
676 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
677 CFRelease(stringRef
);
680 else if (info
[i
].oldItemType
== kSecKeyKeyType
) {
681 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
682 uint32_t keyAlgValue
= *((uint32_t*)list
.attr
[i
].data
);
683 bool retainString
= true;
684 switch (keyAlgValue
) {
685 case CSSM_ALGID_RSA
:
686 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
688 case CSSM_ALGID_DSA
:
689 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
691 case CSSM_ALGID_AES
:
692 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
694 case CSSM_ALGID_DES
:
695 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
697 case CSSM_ALGID_3DES
:
698 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
700 case CSSM_ALGID_RC4
:
701 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
703 case CSSM_ALGID_RC2
:
704 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
706 case CSSM_ALGID_CAST
:
707 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
709 case CSSM_ALGID_ECDSA
:
710 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
713 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyAlgValue
);
714 retainString
= false;
718 if (retainString
) CFRetain(stringRef
);
719 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
720 CFRelease(stringRef
);
724 // normal case: attribute contains a string
725 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
726 if (stringRef
== NULL
)
727 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
728 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
729 CFRelease(stringRef
);
734 case kDataRepresentation
:
736 if ((info
[i
].oldItemType
== kSecKeyLabel
) && (list
.attr
[i
].length
== kUUIDStringLength
)) {
737 // It's possible that there could be a string here because the key label may have a UUID
738 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
739 if (stringRef
== NULL
)
740 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
741 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
742 CFRelease(stringRef
);
745 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*) list
.attr
[i
].data
, list
.attr
[i
].length
);
747 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
748 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dataRef
);
753 case kNumberRepresentation
:
755 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, list
.attr
[i
].data
);
756 if (numberRef
== NULL
)
757 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
758 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), numberRef
);
759 CFRelease(numberRef
);
763 case kBooleanRepresentation
:
765 uint32_t value
= *((uint32_t*)list
.attr
[i
].data
);
766 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
767 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), boolRef
);
771 case kDateRepresentation
:
773 CFDateRef dateRef
= NULL
;
774 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list
.attr
[i
].data
, list
.attr
[i
].length
, &dateRef
);
776 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
777 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dateRef
);
785 SecKeychainItemFreeContent(&list
, NULL
);
795 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
796 * attributes of item.
799 _CreateAttributesDictionaryFromGenericPasswordItem(
800 CFAllocatorRef allocator
,
801 SecKeychainItemRef item
,
802 CFDictionaryRef
*dictionary
)
804 // do the basic allocations
805 CFMutableDictionaryRef dict
= NULL
;
806 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, item
, dict
);
807 if (result
== errSecSuccess
) // did we complete OK
809 CFDictionaryAddValue(dict
, kSecClass
, kSecClassGenericPassword
);
820 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
821 * attributes of item.
824 _CreateAttributesDictionaryFromCertificateItem(
825 CFAllocatorRef allocator
,
826 SecKeychainItemRef item
,
827 CFDictionaryRef
*dictionary
)
829 // do the basic allocations
830 CFMutableDictionaryRef dict
= NULL
;
831 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gCertificateAttributes
, kNumberOfCertificateAttributes
, item
, dict
);
832 if (result
== errSecSuccess
) // did we complete OK
834 CFDictionaryAddValue(dict
, kSecClass
, kSecClassCertificate
);
839 return errSecSuccess
;
843 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
844 * attributes of item.
847 _CreateAttributesDictionaryFromKeyItem(
848 CFAllocatorRef allocator
,
849 SecKeychainItemRef item
,
850 CFDictionaryRef
*dictionary
)
853 //%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
854 // Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
855 // SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
858 goto error_exit
; // unable to get the attribute info (i.e. database schema)
861 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
863 // do the basic allocations
864 CFMutableDictionaryRef dict
= NULL
;
865 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gKeyAttributes
, kNumberOfKeyAttributes
, item
, dict
);
866 if (result
== errSecSuccess
) // did we complete OK
868 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
873 return errSecSuccess
;
876 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
878 SecItemClass itemClass
= (SecItemClass
) 0;
880 SecKeychainAttributeList
*attrList
= NULL
;
881 SecKeychainAttributeInfo
*info
= NULL
;
882 SecKeychainRef keychain
= NULL
;
884 OSStatus status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
886 goto error_exit
; // item must have an itemClass
891 case kSecInternetPasswordItemClass
:
892 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
894 case kSecGenericPasswordItemClass
:
895 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
897 case 'ashp': /* kSecAppleSharePasswordItemClass */
898 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
905 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
907 goto error_exit
; // item must have a keychain, so we can get the attribute info for it
910 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
912 goto error_exit
; // unable to get the attribute info (i.e. database schema)
915 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
917 goto error_exit
; // unable to get the attribute info (i.e. database schema)
920 for (ix
= 0; ix
< info
->count
; ++ix
)
922 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
923 if (!attribute
->length
&& !attribute
->data
)
926 UInt32 j
, count
= kNumberOfKeyAttributes
;
927 InternalAttributeListInfo
*intInfo
= NULL
;
928 for (j
=0; j
<count
; j
++) {
929 if (gKeyAttributes
[j
].oldItemType
== info
->tag
[ix
]) {
930 intInfo
= &gKeyAttributes
[j
];
937 switch (intInfo
->itemRepresentation
)
939 case kStringRepresentation
:
941 CFStringRef stringRef
;
942 if (intInfo
->oldItemType
== kSecKeyKeyClass
) {
943 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
944 UInt32 keyRecordValue
= *((UInt32
*)attribute
->data
);
945 bool retainString
= true;
946 switch (keyRecordValue
) {
947 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
948 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
950 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
951 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
953 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
954 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
957 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyRecordValue
);
958 retainString
= false;
962 if (retainString
) CFRetain(stringRef
);
963 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
964 CFRelease(stringRef
);
967 else if (intInfo
->oldItemType
== kSecKeyKeyType
) {
968 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
969 UInt32 keyAlgValue
= *((UInt32
*)attribute
->data
);
970 bool retainString
= true;
971 switch (keyAlgValue
) {
972 case CSSM_ALGID_RSA
:
973 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
975 case CSSM_ALGID_DSA
:
976 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
978 case CSSM_ALGID_AES
:
979 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
981 case CSSM_ALGID_DES
:
982 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
984 case CSSM_ALGID_3DES
:
985 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
987 case CSSM_ALGID_RC4
:
988 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
990 case CSSM_ALGID_RC2
:
991 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
993 case CSSM_ALGID_CAST
:
994 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
996 case CSSM_ALGID_ECDSA
:
997 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
1000 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyAlgValue
);
1001 retainString
= false;
1005 if (retainString
) CFRetain(stringRef
);
1006 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1007 CFRelease(stringRef
);
1011 // normal case: attribute contains a string
1012 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1013 if (stringRef
== NULL
)
1014 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1015 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1016 CFRelease(stringRef
);
1021 case kDataRepresentation
:
1023 if ((intInfo
->oldItemType
== kSecKeyLabel
) && (attribute
->length
== kUUIDStringLength
)) {
1024 // It's possible that there could be a string here because the key label may have a UUID
1025 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1026 if (stringRef
== NULL
)
1027 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1028 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1029 CFRelease(stringRef
);
1033 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)attribute
->data
, attribute
->length
);
1034 if (dataRef
== NULL
)
1035 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
1036 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dataRef
);
1041 case kNumberRepresentation
:
1043 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attribute
->data
);
1044 if (numberRef
== NULL
)
1045 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
1046 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), numberRef
);
1047 CFRelease(numberRef
);
1051 case kBooleanRepresentation
:
1053 UInt32 value
= *((UInt32
*)attribute
->data
);
1054 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1055 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), boolRef
);
1059 case kDateRepresentation
:
1061 //%%% FIXME need to convert from a CSSM date string to a CFDateRef here
1062 CFDateRef dateRef
= NULL
;
1063 if (dateRef
== NULL
)
1064 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
1065 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dateRef
);
1072 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
1077 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
1080 SecKeychainFreeAttributeInfo(info
);
1083 CFRelease(keychain
);
1092 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
1093 * attributes of item.
1096 _CreateAttributesDictionaryFromInternetPasswordItem(
1097 CFAllocatorRef allocator
,
1098 SecKeychainItemRef item
,
1099 CFDictionaryRef
*dictionary
)
1102 SecKeychainAttribute attr
[] = {
1103 { kSecServerItemAttr
, 0, NULL
}, /* [0] server */
1104 { kSecSecurityDomainItemAttr
, 0, NULL
}, /* [1] securityDomain */
1105 { kSecAccountItemAttr
, 0, NULL
}, /* [2] account */
1106 { kSecPathItemAttr
, 0, NULL
}, /* [3] path */
1107 { kSecPortItemAttr
, 0, NULL
}, /* [4] port */
1108 { kSecProtocolItemAttr
, 0, NULL
}, /* [5] protocol */
1109 { kSecAuthenticationTypeItemAttr
, 0, NULL
}, /* [6] authenticationType */
1110 { kSecCommentItemAttr
, 0, NULL
}, /* [7] comment */
1111 { kSecDescriptionItemAttr
, 0, NULL
}, /* [8] description */
1112 { kSecLabelItemAttr
, 0, NULL
}, /* [9] label */
1113 { kSecCreationDateItemAttr
, 0, NULL
}, /* [10] creation date */
1114 { kSecModDateItemAttr
, 0, NULL
}, /* [11] modification date */
1115 { kSecCreatorItemAttr
, 0, NULL
}, /* [12] creator */
1116 { kSecTypeItemAttr
, 0, NULL
}, /* [13] type */
1117 { kSecInvisibleItemAttr
, 0, NULL
}, /* [14] invisible */
1118 { kSecNegativeItemAttr
, 0, NULL
}, /* [15] negative */
1120 SecKeychainAttributeList attrList
= { sizeof(attr
) / sizeof(SecKeychainAttribute
), attr
};
1123 CFTypeRef keys
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1124 CFTypeRef values
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1128 // copy the item's attributes
1129 status
= SecKeychainItemCopyContent(item
, NULL
, &attrList
, NULL
, NULL
);
1130 require_noerr(status
, SecKeychainItemCopyContent_failed
);
1135 keys
[numValues
] = kSecClass
;
1136 values
[numValues
] = kSecClassInternetPassword
;
1139 // add kSecAttrServer
1140 if ( attrList
.attr
[0].length
> 0 ) {
1141 keys
[numValues
] = kSecAttrServer
;
1142 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[0].data
, attrList
.attr
[0].length
, kCFStringEncodingUTF8
, FALSE
);
1143 if ( values
[numValues
] != NULL
) {
1148 // add kSecAttrSecurityDomain
1149 if ( attrList
.attr
[1].length
> 0 ) {
1150 keys
[numValues
] = kSecAttrSecurityDomain
;
1151 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[1].data
, attrList
.attr
[1].length
, kCFStringEncodingUTF8
, FALSE
);
1152 if ( values
[numValues
] != NULL
) {
1157 // add kSecAttrAccount
1158 if ( attrList
.attr
[2].length
> 0 ) {
1159 keys
[numValues
] = kSecAttrAccount
;
1160 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[2].data
, attrList
.attr
[2].length
, kCFStringEncodingUTF8
, FALSE
);
1161 if ( values
[numValues
] != NULL
) {
1167 if ( attrList
.attr
[3].length
> 0 ) {
1168 keys
[numValues
] = kSecAttrPath
;
1169 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[3].data
, attrList
.attr
[3].length
, kCFStringEncodingUTF8
, FALSE
);
1170 if ( values
[numValues
] != NULL
) {
1176 if ( attrList
.attr
[4].length
> 0 ) {
1177 keys
[numValues
] = kSecAttrPort
;
1178 values
[numValues
] = CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[4].data
);
1179 if ( values
[numValues
] != NULL
) {
1184 // add kSecAttrProtocol
1185 if ( attrList
.attr
[5].length
> 0 ) {
1186 keys
[numValues
] = kSecAttrProtocol
;
1187 values
[numValues
] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType
*)attrList
.attr
[5].data
);
1188 if ( values
[numValues
] != NULL
) {
1189 CFRetain(values
[numValues
]);
1194 // add kSecAttrAuthenticationType
1195 if ( attrList
.attr
[6].length
> 0 ) {
1196 keys
[numValues
] = kSecAttrAuthenticationType
;
1197 values
[numValues
] = _SecAttrAuthenticationTypeForSecAuthenticationType( (SecAuthenticationType
) (*(SecProtocolType
*)attrList
.attr
[6].data
));
1198 if ( values
[numValues
] != NULL
) {
1199 CFRetain(values
[numValues
]);
1204 // add kSecAttrComment
1205 if ( attrList
.attr
[7].length
> 0 ) {
1206 keys
[numValues
] = kSecAttrComment
;
1207 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[7].data
, attrList
.attr
[7].length
, kCFStringEncodingUTF8
, FALSE
);
1208 if ( values
[numValues
] != NULL
) {
1213 // add kSecAttrDescription
1214 if ( attrList
.attr
[8].length
> 0 ) {
1215 keys
[numValues
] = kSecAttrDescription
;
1216 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[8].data
, attrList
.attr
[8].length
, kCFStringEncodingUTF8
, FALSE
);
1217 if ( values
[numValues
] != NULL
) {
1222 // add kSecAttrLabel
1223 if ( attrList
.attr
[9].length
> 0 ) {
1224 keys
[numValues
] = kSecAttrLabel
;
1225 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[9].data
, attrList
.attr
[9].length
, kCFStringEncodingUTF8
, FALSE
);
1226 if ( values
[numValues
] != NULL
) {
1231 // add kSecAttrCreationDate
1232 if ( attrList
.attr
[10].length
> 0 ) {
1233 CFDateRef creationDate
= NULL
;
1234 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[10].data
, attrList
.attr
[10].length
, &creationDate
);
1235 keys
[numValues
] = kSecAttrCreationDate
;
1236 values
[numValues
] = creationDate
;
1237 if ( values
[numValues
] != NULL
) {
1242 // add kSecAttrModificationDate
1243 if ( attrList
.attr
[11].length
> 0 ) {
1244 CFDateRef modDate
= NULL
;
1245 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[11].data
, attrList
.attr
[11].length
, &modDate
);
1246 keys
[numValues
] = kSecAttrModificationDate
;
1247 values
[numValues
] = modDate
;
1248 if ( values
[numValues
] != NULL
) {
1253 // add kSecCreatorItemAttr
1254 if ( attrList
.attr
[12].length
> 0 ) {
1255 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[12].data
);
1256 keys
[numValues
] = kSecAttrCreator
;
1257 values
[numValues
] = numberRef
;
1258 if ( values
[numValues
] != NULL
) {
1259 CFRetain(values
[numValues
]);
1264 // add kSecTypeItemAttr
1265 if ( attrList
.attr
[13].length
> 0 ) {
1266 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[13].data
);
1267 keys
[numValues
] = kSecAttrType
;
1268 values
[numValues
] = numberRef
;
1269 if ( values
[numValues
] != NULL
) {
1270 CFRetain(values
[numValues
]);
1275 // add kSecInvisibleItemAttr
1276 if ( attrList
.attr
[14].length
> 0 ) {
1277 uint32_t value
= *((uint32_t*)attrList
.attr
[14].data
);
1278 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1279 keys
[numValues
] = kSecAttrIsInvisible
;
1280 values
[numValues
] = boolRef
;
1281 if ( values
[numValues
] != NULL
) {
1282 CFRetain(values
[numValues
]);
1287 // add kSecNegativeItemAttr
1288 if ( attrList
.attr
[15].length
> 0 ) {
1289 uint32_t value
= *((uint32_t*)attrList
.attr
[15].data
);
1290 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1291 keys
[numValues
] = kSecAttrIsNegative
;
1292 values
[numValues
] = boolRef
;
1293 if ( values
[numValues
] != NULL
) {
1294 CFRetain(values
[numValues
]);
1299 // create the dictionary
1300 *dictionary
= CFDictionaryCreate(allocator
, keys
, values
, numValues
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1302 // release the values added to the dictionary
1303 for ( index
= 0; index
< numValues
; ++index
)
1305 CFRelease(values
[index
]);
1308 // and free the attributes
1309 (void) SecKeychainItemFreeContent(&attrList
, NULL
);
1311 SecKeychainItemCopyContent_failed
:
1318 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
1319 * attributes of the specified item class and item.
1322 _CreateAttributesDictionaryFromItem(
1323 CFAllocatorRef allocator
,
1324 SecItemClass itemClass
,
1325 SecKeychainItemRef item
,
1326 CFDictionaryRef
*dictionary
)
1330 case kSecInternetPasswordItemClass
:
1331 return _CreateAttributesDictionaryFromInternetPasswordItem(allocator
, item
, dictionary
);
1333 case kSecGenericPasswordItemClass
:
1334 return _CreateAttributesDictionaryFromGenericPasswordItem(allocator
, item
, dictionary
);
1336 case kSecCertificateItemClass
:
1337 return _CreateAttributesDictionaryFromCertificateItem(allocator
, item
, dictionary
);
1339 case kSecPublicKeyItemClass
:
1340 case kSecPrivateKeyItemClass
:
1341 case kSecSymmetricKeyItemClass
:
1342 return _CreateAttributesDictionaryFromKeyItem(allocator
, item
, dictionary
);
1353 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
1354 * by the _CreateSecKeychainAttributeListFromDictionary function.
1358 SecKeychainAttributeList
*attrListPtr
)
1362 if ( attrListPtr
!= NULL
) {
1363 if ( attrListPtr
->attr
!= NULL
) {
1364 // free any attribute data
1365 for ( index
= 0; index
< attrListPtr
->count
; ++index
) {
1366 free(attrListPtr
->attr
[index
].data
);
1368 // free the attribute array
1369 free(attrListPtr
->attr
);
1371 // free the attribute list
1377 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
1378 * attr using the data and tag parameters.
1380 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1381 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1384 _CFDataCreateAttribute(
1386 SecKeychainAttrType tag
,
1387 SecKeychainAttributePtr attr
)
1389 OSStatus status
= errSecSuccess
;
1392 // set the attribute tag
1395 // determine the attribute length
1396 attr
->length
= (UInt32
) CFDataGetLength(data
);
1397 range
= CFRangeMake(0, (CFIndex
)attr
->length
);
1399 // allocate memory for the attribute bytes
1400 attr
->data
= malloc(attr
->length
);
1401 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1403 // get the attribute bytes
1404 CFDataGetBytes(data
, range
, (UInt8
*)attr
->data
);
1412 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
1413 * attr using the string and tag parameters.
1415 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1416 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1419 _CFStringCreateAttribute(
1421 SecKeychainAttrType tag
,
1422 SecKeychainAttributePtr attr
)
1424 OSStatus status
= errSecSuccess
;
1427 // set the attribute tag
1430 // determine the attribute length
1431 range
= CFRangeMake(0, CFStringGetLength(string
));
1432 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, (CFIndex
*)&attr
->length
);
1434 // allocate memory for the attribute bytes
1435 attr
->data
= malloc(attr
->length
);
1436 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1438 // get the attribute bytes
1439 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*)attr
->data
, attr
->length
, NULL
);
1448 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1449 * from the attribute key/values in attrDictionary.
1451 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1452 * must be freed by the caller with _FreeAttrList()
1455 _CreateSecKeychainGenericPasswordAttributeListFromDictionary(
1456 CFDictionaryRef attrDictionary
,
1457 SecKeychainAttributeList
**attrList
)
1459 return _ConvertNewFormatToOldFormat(NULL
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, attrDictionary
, *attrList
);
1464 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
1465 * from the attribute key/values in attrDictionary.
1467 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1468 * must be freed by the caller with _FreeAttrList()
1471 _CreateSecKeychainCertificateAttributeListFromDictionary(
1472 CFDictionaryRef attrDictionary
,
1473 SecKeychainAttributeList
**attrList
)
1475 return _ConvertNewFormatToOldFormat(NULL
, gCertificateAttributes
, kNumberOfCertificateAttributes
, attrDictionary
, *attrList
);
1480 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
1481 * from the attribute key/values in attrDictionary.
1483 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1484 * must be freed by the caller with _FreeAttrList()
1487 _CreateSecKeychainKeyAttributeListFromDictionary(
1488 CFDictionaryRef attrDictionary
,
1489 SecKeychainAttributeList
**attrList
)
1492 //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
1493 return _ConvertNewFormatToOldFormat(NULL
, gKeyAttributes
, kNumberOfKeyAttributes
, attrDictionary
, *attrList
);
1495 // explicitly build attribute list for supported key attributes
1496 // NOTE: this code supports only MaxSecKeyAttributes (15) attributes
1497 const int MaxSecKeyAttributes
= 15;
1501 SecKeychainAttributeList
*attrListPtr
;
1503 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1504 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1506 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeyAttributes
, sizeof(SecKeychainAttribute
));
1507 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1509 // [0] get the kSecKeyKeyClass value
1510 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyClass
, (const void **)&value
) && value
) {
1511 UInt32 keyRecordValue
= 0;
1512 if (CFEqual(kSecAttrKeyClassPublic
, value
))
1513 keyRecordValue
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
1514 else if (CFEqual(kSecAttrKeyClassPrivate
, value
))
1515 keyRecordValue
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
1516 else if (CFEqual(kSecAttrKeyClassSymmetric
, value
))
1517 keyRecordValue
= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
;
1519 // only use this attribute if we recognize the value!
1520 if (keyRecordValue
!= 0) {
1521 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1522 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1524 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyClass
;
1525 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1526 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyRecordValue
;
1528 ++attrListPtr
->count
;
1532 // [1] get the kSecKeyPrintName string
1533 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) && value
) {
1534 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyPrintName
, &attrListPtr
->attr
[attrListPtr
->count
]);
1535 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1537 ++attrListPtr
->count
;
1540 // [2] get the kSecKeyPermanent boolean
1541 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsPermanent
, (const void **)&value
) && value
) {
1542 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1543 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1545 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyPermanent
;
1546 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1547 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1549 ++attrListPtr
->count
;
1552 // [3] get the kSecKeyLabel string
1553 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationLabel
, (const void **)&value
) && value
) {
1554 if (CFStringGetTypeID() == CFGetTypeID(value
))
1555 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1556 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1557 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1559 status
= errSecParam
;
1561 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1563 ++attrListPtr
->count
;
1566 // [4] get the kSecKeyApplicationTag data
1567 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationTag
, (const void **)&value
) && value
) {
1568 if (CFStringGetTypeID() == CFGetTypeID(value
))
1569 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1570 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1571 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1573 status
= errSecParam
;
1575 require_noerr_quiet(status
, CFDataCreateAttribute_failed
);
1576 ++attrListPtr
->count
;
1579 // [5] get the kSecKeyKeyType number
1580 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyType
, (const void **)&value
) && value
) {
1581 UInt32 keyAlgValue
= _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType
);
1582 if (keyAlgValue
!= 0) {
1583 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1584 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1586 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyType
;
1587 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1588 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyAlgValue
;
1590 ++attrListPtr
->count
;
1594 // [6] get the kSecKeyKeySizeInBits number
1595 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeySizeInBits
, (const void **)&value
) && value
) {
1596 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1597 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1598 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1600 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeySizeInBits
;
1601 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1602 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1604 ++attrListPtr
->count
;
1608 // [7] get the kSecKeyEffectiveKeySize number
1609 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrEffectiveKeySize
, (const void **)&value
) && value
) {
1610 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1611 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1612 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1614 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEffectiveKeySize
;
1615 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1616 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1618 ++attrListPtr
->count
;
1622 // [8] get the kSecKeyEncrypt boolean
1623 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanEncrypt
, (const void **)&value
) && value
) {
1624 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1625 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1626 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1628 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEncrypt
;
1629 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1630 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1632 ++attrListPtr
->count
;
1636 // [9] get the kSecKeyDecrypt boolean
1637 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDecrypt
, (const void **)&value
) && value
) {
1638 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1639 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1640 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1642 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDecrypt
;
1643 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1644 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1646 ++attrListPtr
->count
;
1650 // [10] get the kSecKeyDerive boolean
1651 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDerive
, (const void **)&value
) && value
) {
1652 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1653 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1654 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1656 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDerive
;
1657 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1658 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1660 ++attrListPtr
->count
;
1664 // [11] get the kSecKeySign boolean
1665 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanSign
, (const void **)&value
) && value
) {
1666 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1667 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1668 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1670 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeySign
;
1671 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1672 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1674 ++attrListPtr
->count
;
1678 // [12] get the kSecKeyVerify boolean
1679 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanVerify
, (const void **)&value
) && value
) {
1680 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1681 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1682 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1684 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyVerify
;
1685 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1686 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1688 ++attrListPtr
->count
;
1692 // [13] get the kSecKeyWrap boolean
1693 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanWrap
, (const void **)&value
) && value
) {
1694 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1695 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1696 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1698 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyWrap
;
1699 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1700 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1702 ++attrListPtr
->count
;
1706 // [14] get the kSecKeyUnwrap boolean
1707 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanUnwrap
, (const void **)&value
) && value
) {
1708 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1709 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1710 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1712 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyUnwrap
;
1713 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1714 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1716 ++attrListPtr
->count
;
1720 // return the pointer to the attrList
1721 *attrList
= attrListPtr
;
1723 return ( errSecSuccess
);
1727 malloc_number_failed
:
1728 CFDataCreateAttribute_failed
:
1729 CFStringCreateAttribute_failed
:
1730 malloc_attrPtr_failed
:
1732 // free any attributes
1733 _FreeAttrList(attrListPtr
);
1735 calloc_attrListPtr_failed
:
1737 return ( errSecBufferTooSmall
);
1742 static CFTypeRef
copyNumber(CFTypeRef obj
)
1747 CFTypeID tid
= CFGetTypeID(obj
);
1748 if (tid
== CFNumberGetTypeID())
1754 if (tid
== CFBooleanGetTypeID())
1756 SInt32 value
= CFBooleanGetValue((CFBooleanRef
)obj
);
1757 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1760 if (tid
== CFStringGetTypeID())
1762 SInt32 value
= CFStringGetIntValue((CFStringRef
)obj
);
1763 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
1764 /* If a string converted to an int isn't equal to the int printed as
1765 a string, return a NULL instead. */
1766 if (!CFEqual(t
, obj
))
1772 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1778 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1779 * from the attribute key/values in attrDictionary.
1781 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1782 * must be freed by the caller with _FreeAttrList()
1785 _CreateSecKeychainInternetPasswordAttributeListFromDictionary(
1786 CFDictionaryRef attrDictionary
,
1787 SecKeychainAttributeList
**attrList
)
1789 // explicitly build attribute list for supported key attributes
1790 // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
1791 const int MaxSecKeychainAttributes
= 14;
1795 SecKeychainAttributeList
*attrListPtr
;
1797 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1798 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1800 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeychainAttributes
, sizeof(SecKeychainAttribute
));
1801 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1804 // [0] get the serverName string
1805 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrServer
, (const void **)&value
) ) {
1806 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecServerItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1807 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1809 ++attrListPtr
->count
;
1812 // [1] get the securityDomain string
1813 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrSecurityDomain
, (const void **)&value
) ) {
1814 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecSecurityDomainItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1815 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1817 ++attrListPtr
->count
;
1820 // [2] get the accountName string
1821 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAccount
, (const void **)&value
) ) {
1822 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecAccountItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1823 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1825 ++attrListPtr
->count
;
1828 // [3] get the path string
1829 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPath
, (const void **)&value
) ) {
1830 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecPathItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1831 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1833 ++attrListPtr
->count
;
1836 // [4] get the port number
1837 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPort
, (const void **)&value
) ) {
1838 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt16
));
1839 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1841 CFTypeRef num
= copyNumber(value
);
1842 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1843 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecPortItemAttr
;
1844 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt16
);
1845 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt16Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1848 ++attrListPtr
->count
;
1851 // [5] get the protocol
1852 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrProtocol
, (const void **)&value
) ) {
1853 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecProtocolType
));
1854 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_protocol_failed
, status
= errSecBufferTooSmall
);
1856 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecProtocolItemAttr
;
1857 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecProtocolType
);
1858 *(SecProtocolType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecProtocolTypeForSecAttrProtocol(value
);
1860 ++attrListPtr
->count
;
1863 // [6] get the authenticationType
1864 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAuthenticationType
, (const void **)&value
) ) {
1865 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecAuthenticationType
));
1866 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_authenticationType_failed
, status
= errSecBufferTooSmall
);
1868 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecAuthenticationTypeItemAttr
;
1869 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecAuthenticationType
);
1870 *(SecAuthenticationType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecAuthenticationTypeForSecAttrAuthenticationType(value
);
1872 ++attrListPtr
->count
;
1875 // [7] get the comment string
1876 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrComment
, (const void **)&value
) ) {
1877 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecCommentItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1878 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1880 ++attrListPtr
->count
;
1883 // [8] get the description string
1884 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrDescription
, (const void **)&value
) ) {
1885 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecDescriptionItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1886 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1888 ++attrListPtr
->count
;
1891 // [9] get the label string
1892 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) ) {
1893 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecLabelItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1894 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1896 ++attrListPtr
->count
;
1899 // [10] get the creator code
1900 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCreator
, (const void **)&value
) ) {
1901 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1902 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1904 CFTypeRef num
= copyNumber(value
);
1905 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1906 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecCreatorItemAttr
;
1907 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1908 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1911 ++attrListPtr
->count
;
1914 // [11] get the type code
1915 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrType
, (const void **)&value
) ) {
1916 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1917 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1919 CFTypeRef num
= copyNumber(value
);
1920 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1921 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecTypeItemAttr
;
1922 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1923 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1926 ++attrListPtr
->count
;
1929 // [12] get the invisible flag
1930 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsInvisible
, (const void **)&value
) ) {
1931 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1932 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1934 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecInvisibleItemAttr
;
1935 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1936 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1938 ++attrListPtr
->count
;
1941 // [13] get the negative flag
1942 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsNegative
, (const void **)&value
) ) {
1943 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1944 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1946 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecNegativeItemAttr
;
1947 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1948 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1950 ++attrListPtr
->count
;
1953 // return the pointer to the attrList
1954 *attrList
= attrListPtr
;
1956 return ( errSecSuccess
);
1960 malloc_authenticationType_failed
:
1961 malloc_protocol_failed
:
1963 CFStringCreateAttribute_failed
:
1964 malloc_attrPtr_failed
:
1966 // free any attributes
1967 _FreeAttrList(attrListPtr
);
1969 calloc_attrListPtr_failed
:
1971 return ( errSecBufferTooSmall
);
1976 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
1977 * from the attribute key/values in attrDictionary for the specified item class.
1979 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1980 * must be freed by the caller with _FreeAttrList()
1983 _CreateSecKeychainAttributeListFromDictionary(
1984 CFDictionaryRef attrDictionary
,
1985 SecItemClass itemClass
,
1986 SecKeychainAttributeList
**attrList
)
1990 case kSecInternetPasswordItemClass
:
1991 return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1993 case kSecGenericPasswordItemClass
:
1994 return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1996 case kSecCertificateItemClass
:
1997 return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary
, attrList
);
1999 case kSecPublicKeyItemClass
:
2000 case kSecPrivateKeyItemClass
:
2001 case kSecSymmetricKeyItemClass
:
2002 return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary
, attrList
);
2012 * _AppNameFromSecTrustedApplication attempts to pull the name of the
2013 * application/tool from the SecTrustedApplicationRef.
2015 static CFStringRef CF_RETURNS_RETAINED
2016 _AppNameFromSecTrustedApplication(
2017 CFAllocatorRef alloc
,
2018 SecTrustedApplicationRef appRef
)
2022 CFDataRef appDataRef
;
2026 // get the data for item's application/tool
2027 status
= SecTrustedApplicationCopyData(appRef
, &appDataRef
);
2028 if ( status
== errSecSuccess
) {
2031 // convert it to a CFString potentially containing the path
2032 path
= CFStringCreateWithCString(NULL
, (char *)CFDataGetBytePtrVoid(appDataRef
), kCFStringEncodingUTF8
);
2033 if ( path
!= NULL
) {
2034 // the path has to start with a "/" and cannot contain "://"
2035 if ( CFStringHasPrefix(path
, CFSTR("/")) && (CFStringFind(path
, CFSTR("://"), 0).location
== kCFNotFound
) ) {
2036 CFRange nameRange
, compRg
;
2038 nameRange
= CFRangeMake(0, CFStringGetLength(path
));
2040 // remove the trailing slashes (if any)
2041 while ( (nameRange
.length
> 0) && (CFStringGetCharacterAtIndex(path
, nameRange
.length
- 1) == '/') ) {
2042 nameRange
.length
--;
2045 if ( nameRange
.length
> 0 ) {
2046 // find last slash and adjust nameRange to be everything after it
2047 if ( CFStringFindWithOptions(path
, CFSTR("/"), nameRange
, kCFCompareBackwards
, &compRg
) ) {
2048 nameRange
.length
= nameRange
.location
+ nameRange
.length
- (compRg
.location
+ 1);
2049 nameRange
.location
= compRg
.location
+ 1;
2052 result
= CFStringCreateWithSubstring(alloc
, path
, nameRange
);
2057 CFRelease(appDataRef
);
2063 /* (This function really belongs in SecIdentity.cpp!)
2065 * Returns the public key item corresponding to the identity, if it exists in
2066 * the same keychain as the private key. Note that the public key might not
2067 * exist in the same keychain (e.g. if the identity was imported via PKCS12),
2068 * in which case it will not be found.
2071 _SecIdentityCopyPublicKey(
2072 SecIdentityRef identityRef
,
2073 SecKeyRef
*publicKeyRef
)
2077 SecKeychainAttribute attr
= { kSecKeyLabel
, 0, NULL
};
2078 SecKeychainAttributeList attrList
= { 1, &attr
};
2079 SecKeychainAttributeList
*keyAttrList
= NULL
;
2080 SecKeychainAttributeInfo
*info
= NULL
;
2081 SecKeychainSearchRef search
= NULL
;
2082 SecKeychainRef keychain
= NULL
;
2083 SecKeychainItemRef privateKey
= NULL
;
2084 SecKeychainItemRef publicKey
= NULL
;
2086 status
= SecIdentityCopyPrivateKey(identityRef
, (SecKeyRef
*)&privateKey
);
2088 goto error_exit
; // identity must have a private key
2090 status
= SecKeychainItemCopyKeychain(privateKey
, &keychain
);
2092 goto error_exit
; // private key must have a keychain, so we can get the attribute info for it
2094 status
= SecKeychainAttributeInfoForItemID(keychain
, kSecPrivateKeyItemClass
, &info
);
2096 goto error_exit
; // unable to get the attribute info (i.e. database schema) for private keys
2098 status
= SecKeychainItemCopyAttributesAndData(privateKey
, info
, NULL
, &keyAttrList
, NULL
, NULL
);
2100 goto error_exit
; // unable to get the key label attribute for the private key
2103 // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
2104 for (count
= 0; count
< keyAttrList
->count
; count
++) {
2105 if (keyAttrList
->attr
[count
].tag
== kSecKeyLabel
) {
2106 attr
.length
= keyAttrList
->attr
[count
].length
;
2107 attr
.data
= keyAttrList
->attr
[count
].data
;
2111 if (!attr
.length
|| !attr
.data
) {
2112 status
= errSecNoSuchAttr
;
2113 goto error_exit
; // the private key didn't have the hash of the public key in its kSecKeyLabel
2115 status
= SecKeychainSearchCreateFromAttributes(keychain
, kSecPublicKeyItemClass
, &attrList
, &search
);
2117 goto error_exit
; // unable to create the search reference
2119 status
= SecKeychainSearchCopyNext(search
, &publicKey
);
2121 goto error_exit
; // unable to find the public key
2125 *publicKeyRef
= (SecKeyRef
)publicKey
;
2127 CFRelease(publicKey
);
2130 if (status
!= errSecSuccess
) {
2132 *publicKeyRef
= NULL
;
2134 CFRelease(publicKey
);
2140 SecKeychainItemFreeAttributesAndData(keyAttrList
, NULL
);
2143 SecKeychainFreeAttributeInfo(info
);
2146 CFRelease(keychain
);
2149 CFRelease(privateKey
);
2156 * Deletes a keychain item if the current application/tool is the only application/tool
2157 * with decrypt access to that keychain item. If more than one application/tool
2158 * has decrypt access to the keychain item, the item is left on the keychain.
2160 * TBD: If more than one app/tool has access to the keychain item, we should remove
2161 * the current app/tool's decrypt access. There's no easy way to do that with
2162 * current keychain APIs without bringing up the security UI.
2165 _SafeSecKeychainItemDelete(
2166 SecKeychainItemRef itemRef
)
2169 SecAccessRef access
= NULL
;
2170 CFArrayRef aclList
= NULL
;
2171 SecACLRef acl
= NULL
;
2172 CFArrayRef appList
= NULL
;
2173 CFStringRef description
= NULL
;
2174 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
;
2175 CFIndex idx
, count
= 0;
2176 SecTrustedApplicationRef currentAppRef
= NULL
;
2177 CFStringRef itemAppName
= NULL
, currentAppName
= NULL
;
2179 SecItemClass itemClass
= (SecItemClass
)0;
2180 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2181 if (!(itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2182 // only perform the access control safety check on deletion of password credentials;
2183 // if the item is of some other type, delete it normally.
2184 return SecKeychainItemDelete(itemRef
);
2187 // skip access control checking for web form passwords: <rdar://10957301>
2188 // This permits Safari to manage the removal of all web form passwords,
2189 // regardless of whether they are shared by multiple applications.
2190 if (itemClass
== kSecInternetPasswordItemClass
) {
2191 UInt32 tags
[1] = { kSecAuthenticationTypeItemAttr
};
2192 SecKeychainAttributeInfo attrInfo
= { 1, tags
, NULL
};
2193 SecKeychainAttributeList
*attrs
= NULL
;
2194 status
= SecKeychainItemCopyAttributesAndData(itemRef
, &attrInfo
, NULL
, &attrs
, NULL
, NULL
);
2195 if (!status
&& attrs
) {
2196 bool webFormPassword
= (attrs
->attr
[0].length
== 4 && (!memcmp(attrs
->attr
[0].data
, "form", 4)));
2197 SecKeychainItemFreeAttributesAndData(attrs
, NULL
);
2198 if (webFormPassword
) {
2199 return SecKeychainItemDelete(itemRef
);
2204 // copy the access of the keychain item
2205 status
= SecKeychainItemCopyAccess(itemRef
, &access
);
2206 require_noerr(status
, finish
);
2207 require_quiet(access
!= NULL
, finish
);
2209 // copy the decrypt access control lists -- this is what has access to the keychain item
2210 status
= SecAccessCopySelectedACLList(access
, CSSM_ACL_AUTHORIZATION_DECRYPT
, &aclList
);
2211 require_noerr(status
, finish
);
2212 require_quiet(aclList
!= NULL
, finish
);
2214 // get the access control list
2215 acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, 0);
2216 require_quiet(acl
!= NULL
, finish
);
2218 // copy the application list, description, and CSSM prompt selector for a given access control list entry
2219 status
= SecACLCopySimpleContents(acl
, &appList
, &description
, &promptSelector
);
2220 require_noerr(status
, finish
);
2221 require_quiet(appList
!= NULL
, finish
);
2223 // does the calling application/tool have decrypt access to this item?
2224 count
= CFArrayGetCount(appList
);
2225 for ( idx
= 0; idx
< count
; idx
++ ) {
2226 // get SecTrustedApplicationRef for this entry
2227 SecTrustedApplicationRef itemAppRef
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(appList
, idx
);
2228 require_quiet(itemAppRef
!= NULL
, finish
);
2230 // copy the name out
2231 CFReleaseSafe(itemAppName
);
2232 itemAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), itemAppRef
);
2233 if (itemAppName
== NULL
) {
2235 * If there is no app name, it's probably because it's not an appname
2236 * in the ACE but an entitlement/info.plist based rule instead;
2237 * just let the caller have it. */
2242 // create SecTrustedApplicationRef for current application/tool
2243 CFReleaseNull(currentAppRef
);
2244 status
= SecTrustedApplicationCreateFromPath(NULL
, ¤tAppRef
);
2245 require_noerr(status
, finish
);
2246 require_quiet(currentAppRef
!= NULL
, finish
);
2248 // copy the name out
2249 CFReleaseSafe(currentAppName
);
2250 currentAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), currentAppRef
);
2251 require_quiet(currentAppName
!= NULL
, finish
);
2253 // compare the names to see if we own the decrypt access
2254 // TBD: validation of membership in an application group
2255 if ( CFStringCompare(currentAppName
, itemAppName
, 0) == kCFCompareEqualTo
) {
2263 CFReleaseSafe(currentAppName
);
2264 CFReleaseSafe(itemAppName
);
2265 CFReleaseSafe(currentAppRef
);
2266 CFReleaseSafe(description
);
2267 CFReleaseSafe(appList
);
2268 CFReleaseSafe(aclList
);
2269 CFReleaseSafe(access
);
2271 if ((count
== 0) || (status
== errSecVerifyFailed
)) {
2272 // no "owners" remain in the ACL list (or unable to get ACL)
2273 status
= SecKeychainItemDelete(itemRef
);
2275 // caller is not the "owner" of the item
2276 status
= errSecInvalidOwnerEdit
;
2283 _ReplaceKeychainItem(
2284 SecKeychainItemRef itemToUpdate
,
2285 SecKeychainAttributeList
*changeAttrList
,
2290 SecItemClass itemClass
;
2291 SecKeychainAttributeInfo
*info
= NULL
;
2292 SecKeychainAttributeList
*attrList
= NULL
;
2293 SecKeychainAttributeList newAttrList
= { 0, NULL
};
2294 SecKeychainRef keychain
= NULL
;
2295 SecKeychainItemRef newItem
= NULL
;
2297 int priority
= LOG_DEBUG
;
2298 const char *format
= "ReplaceKeychainItem (%d) error %d";
2300 // get existing item's keychain
2301 status
= SecKeychainItemCopyKeychain(itemToUpdate
, &keychain
);
2302 if (status
) { secitemlog(priority
, format
, 1, (int)status
); }
2303 require_noerr(status
, replace_failed
);
2305 // get attribute info (i.e. database schema) for the item class
2306 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2307 if (status
) { secitemlog(priority
, format
, 2, (int)status
); }
2308 require_noerr(status
, replace_failed
);
2312 case kSecInternetPasswordItemClass
:
2313 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
2315 case kSecGenericPasswordItemClass
:
2316 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
2322 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
2323 if (status
) { secitemlog(priority
, format
, 3, (int)status
); }
2325 // get item's existing attributes (but not data!)
2326 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, info
, &itemClass
, &attrList
, NULL
, NULL
);
2327 if (status
) { secitemlog(priority
, format
, 4, (int)status
); }
2328 require(attrList
!= NULL
, replace_failed
);
2330 // move aside the item by changing a primary attribute
2331 // (currently only for passwords)
2332 if (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
) {
2333 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
2334 CFStringRef uuidStr
= (uuid
) ? CFUUIDCreateString(kCFAllocatorDefault
, uuid
) : CFSTR("MOVED");
2335 CFReleaseSafe(uuid
);
2337 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(uuidStr
), kCFStringEncodingUTF8
) + 1;
2338 char* buffer
= (char*) malloc(maxLength
);
2340 if (CFStringGetCString(uuidStr
, buffer
, maxLength
, kCFStringEncodingUTF8
)) {
2341 UInt32 length
= (UInt32
)strlen(buffer
);
2342 SecKeychainAttribute attrs
[] = { { kSecAccountItemAttr
, length
, (char*)buffer
}, };
2343 SecKeychainAttributeList updateAttrList
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
2344 status
= SecKeychainItemModifyAttributesAndData(itemToUpdate
, &updateAttrList
, 0, NULL
);
2345 if (status
) { secitemlog(priority
, format
, 5, (int)status
); }
2346 if (status
== errSecVerifyFailed
) {
2347 // still unable to change attrs? delete unconditionally here
2348 status
= SecKeychainItemDelete(itemToUpdate
);
2349 if (status
) { secitemlog(priority
, format
, 6, (int)status
); }
2354 CFReleaseSafe(uuidStr
);
2357 require_noerr(status
, replace_failed
);
2359 // make attribute list for new item (the data is still owned by attrList)
2360 newAttrList
.count
= attrList
->count
;
2361 newAttrList
.attr
= (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * attrList
->count
);
2363 for (i
=0, newCount
=0; i
< attrList
->count
; i
++) {
2364 if (attrList
->attr
[i
].length
> 0) {
2365 newAttrList
.attr
[newCount
++] = attrList
->attr
[i
];
2367 // debugging code to log item attributes
2368 SecKeychainAttrType tag
= attrList
->attr
[i
].tag
;
2369 SecKeychainAttrType htag
=(SecKeychainAttrType
)OSSwapConstInt32(tag
);
2370 char tmp
[sizeof(SecKeychainAttrType
) + 1];
2371 char tmpdata
[attrList
->attr
[i
].length
+ 1];
2372 memcpy(tmp
, &htag
, sizeof(SecKeychainAttrType
));
2373 tmp
[sizeof(SecKeychainAttrType
)]=0;
2374 memcpy(tmpdata
, attrList
->attr
[i
].data
, attrList
->attr
[i
].length
);
2375 tmpdata
[attrList
->attr
[i
].length
]=0;
2376 secitemlog(priority
, "item attr '%s' = %d bytes: \"%s\"",
2377 tmp
, (int)attrList
->attr
[i
].length
, tmpdata
);
2381 newAttrList
.count
= newCount
;
2383 // create new item in the same keychain
2384 status
= SecKeychainItemCreateFromContent(itemClass
, &newAttrList
,
2385 (UInt32
)((itemData
) ? CFDataGetLength(itemData
) : 0),
2386 (const void *)((itemData
) ? CFDataGetBytePtr(itemData
) : NULL
),
2387 keychain
, NULL
, &newItem
);
2388 if (status
) { secitemlog(priority
, format
, 7, (int)status
); }
2389 require_noerr(status
, replace_failed
);
2391 // delete the old item unconditionally once new item exists
2392 status
= SecKeychainItemDelete(itemToUpdate
);
2394 // update the new item with changed attributes, if any
2395 status
= (changeAttrList
) ? SecKeychainItemModifyContent(newItem
, changeAttrList
, 0, NULL
) : errSecSuccess
;
2396 if (status
) { secitemlog(priority
, format
, 8, (int)status
); }
2397 if (status
== errSecSuccess
) {
2398 // say the item already exists, because it does now. <rdar://19063674>
2399 status
= errSecDuplicateItem
;
2403 if (newAttrList
.attr
) {
2404 free(newAttrList
.attr
);
2407 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
2410 SecKeychainFreeAttributeInfo(info
);
2412 CFReleaseSafe(newItem
);
2413 CFReleaseSafe(keychain
);
2419 _UpdateKeychainItem(CFTypeRef item
, CFDictionaryRef changedAttributes
)
2421 // This function updates a single keychain item, which may be specified as
2422 // a reference, persistent reference or attribute dictionary, with the
2423 // attributes provided.
2425 OSStatus status
= errSecSuccess
;
2430 SecItemClass itemClass
= (SecItemClass
) 0;
2431 SecAccessRef access
= NULL
;
2432 SecKeychainAttributeList
*changeAttrList
= NULL
;
2433 SecKeychainItemRef itemToUpdate
= NULL
;
2434 CFDataRef theData
= NULL
;
2435 CFTypeID itemType
= CFGetTypeID(item
);
2437 // validate input item (must be convertible to a SecKeychainItemRef)
2438 if (SecKeychainItemGetTypeID() == itemType
||
2439 SecCertificateGetTypeID() == itemType
||
2440 SecKeyGetTypeID() == itemType
) {
2441 // item is already a reference, retain it
2442 itemToUpdate
= (SecKeychainItemRef
) CFRetain(item
);
2444 else if (CFDataGetTypeID() == itemType
) {
2445 // item is a persistent reference, must convert it
2446 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToUpdate
);
2448 else if (CFDictionaryGetTypeID() == itemType
) {
2449 // item is a dictionary
2450 CFTypeRef value
= NULL
;
2451 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2452 // kSecValueRef value is a SecKeychainItemRef, retain it
2453 itemToUpdate
= (SecKeychainItemRef
) CFRetain(value
);
2455 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2456 // kSecValuePersistentRef value is a persistent reference, must convert it
2457 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToUpdate
);
2460 else if (SecIdentityGetTypeID() == itemType
) {
2461 // item is a certificate + private key; since we can't really change the
2462 // certificate's attributes, assume we want to update the private key
2463 status
= SecIdentityCopyPrivateKey((SecIdentityRef
)item
, (SecKeyRef
*)&itemToUpdate
);
2465 require_action(itemToUpdate
!= NULL
, update_failed
, status
= errSecInvalidItemRef
);
2466 require_noerr(status
, update_failed
);
2468 status
= SecKeychainItemCopyContent(itemToUpdate
, &itemClass
, NULL
, NULL
, NULL
);
2469 require_noerr(status
, update_failed
);
2471 // build changeAttrList from changedAttributes dictionary
2474 case kSecInternetPasswordItemClass
:
2476 status
= _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2477 require_noerr(status
, update_failed
);
2481 case kSecGenericPasswordItemClass
:
2483 status
= _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2484 require_noerr(status
, update_failed
);
2488 case kSecCertificateItemClass
:
2490 status
= _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2491 require_noerr(status
, update_failed
);
2495 case kSecPublicKeyItemClass
:
2496 case kSecPrivateKeyItemClass
:
2497 case kSecSymmetricKeyItemClass
:
2499 status
= _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2500 require_noerr(status
, update_failed
);
2503 case kSecAppleSharePasswordItemClass
:
2505 // do nothing (legacy behavior).
2512 // (if the caller is not updating the password, this value will be NULL)
2513 theData
= (CFDataRef
)CFDictionaryGetValue(changedAttributes
, kSecValueData
);
2514 if (theData
!= NULL
) {
2515 require_action(CFDataGetTypeID() == CFGetTypeID(theData
), update_failed
, status
= errSecParam
);
2518 status
= SecKeychainItemModifyContent(itemToUpdate
,
2519 (!changeAttrList
|| changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2520 (theData
!= NULL
) ? (UInt32
)CFDataGetLength(theData
) : 0,
2521 (theData
!= NULL
) ? CFDataGetBytePtrVoid(theData
) : NULL
);
2522 require_noerr(status
, update_failed
);
2524 // one more thing... update access?
2525 if (CFDictionaryGetValueIfPresent(changedAttributes
, kSecAttrAccess
, (const void **)&access
)) {
2526 status
= SecKeychainItemSetAccess(itemToUpdate
, access
);
2530 if (status
== errSecVerifyFailed
&&
2531 (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2532 // if we got a cryptographic failure updating a password item, it needs to be replaced
2533 status
= _ReplaceKeychainItem(itemToUpdate
,
2534 (!changeAttrList
|| changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2538 CFRelease(itemToUpdate
);
2539 _FreeAttrList(changeAttrList
);
2544 _DeleteKeychainItem(CFTypeRef item
)
2546 // This function deletes a single keychain item, which may be specified as
2547 // a reference, persistent reference or attribute dictionary. It will not
2548 // delete non-keychain items or aggregate items (such as a SecIdentityRef);
2549 // it is assumed that the caller will pass identity components separately.
2551 OSStatus status
= errSecSuccess
;
2556 SecKeychainItemRef itemToDelete
= NULL
;
2557 CFTypeID itemType
= CFGetTypeID(item
);
2558 if (SecKeychainItemGetTypeID() == itemType
||
2559 SecCertificateGetTypeID() == itemType
||
2560 SecKeyGetTypeID() == itemType
) {
2561 // item is already a reference, retain it
2562 itemToDelete
= (SecKeychainItemRef
) CFRetain(item
);
2564 else if (CFDataGetTypeID() == itemType
) {
2565 // item is a persistent reference, must convert it
2566 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToDelete
);
2568 else if (CFDictionaryGetTypeID() == itemType
) {
2569 // item is a dictionary
2570 CFTypeRef value
= NULL
;
2571 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2572 // kSecValueRef value is a SecKeychainItemRef, retain it
2573 itemToDelete
= (SecKeychainItemRef
) CFRetain(value
);
2575 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2576 // kSecValuePersistentRef value is a persistent reference, must convert it
2577 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToDelete
);
2583 status
= _SafeSecKeychainItemDelete(itemToDelete
);
2585 CFRelease(itemToDelete
);
2592 _DeleteIdentity(SecIdentityRef identity
)
2594 OSStatus status
, result
= errSecSuccess
;
2595 SecKeyRef privateKey
= NULL
;
2596 SecCertificateRef certificate
= NULL
;
2598 status
= SecIdentityCopyPrivateKey(identity
, &privateKey
);
2600 SecKeyRef publicKey
= NULL
;
2601 status
= _SecIdentityCopyPublicKey(identity
, &publicKey
);
2603 status
= _DeleteKeychainItem(publicKey
);
2604 CFRelease(publicKey
);
2606 status
= _DeleteKeychainItem(privateKey
);
2609 if (privateKey
) CFRelease(privateKey
);
2610 if (status
) result
= status
;
2612 status
= SecIdentityCopyCertificate(identity
, &certificate
);
2614 status
= _DeleteKeychainItem(certificate
);
2617 if (certificate
) CFRelease(certificate
);
2618 if (status
) result
= status
;
2624 _UpdateAggregateStatus(OSStatus newStatus
, OSStatus curStatus
, OSStatus baseStatus
)
2626 // This function is used when atomically processing multiple items,
2627 // where an overall error result must be returned for the entire operation.
2628 // When newStatus is something other than errSecSuccess, we want to keep the "most
2629 // interesting" status (which usually will be newStatus, unless curStatus is
2630 // already set; in that case, newStatus can trump curStatus only by being
2631 // something different than baseStatus.)
2633 OSStatus result
= curStatus
;
2635 if (newStatus
!= errSecSuccess
) {
2637 if (curStatus
!= errSecSuccess
) {
2638 result
= (newStatus
!= baseStatus
) ? newStatus
: curStatus
;
2645 _AddDictValueToOtherDict(const void *key
, const void *value
, void *context
)
2647 // CFDictionaryApplierFunction
2648 // This function just takes the given key/value pair,
2649 // and adds it to another dictionary supplied in the context argument.
2651 CFMutableDictionaryRef dict
= *((CFMutableDictionaryRef
*) context
);
2653 CFDictionaryAddValue(dict
, key
, value
);
2657 static CFStringCompareFlags
2658 _StringCompareFlagsFromQuery(CFDictionaryRef query
)
2661 CFStringCompareFlags flags
= 0;
2662 if (!query
) return flags
;
2664 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&value
) ||
2665 CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2666 flags
|= kCFCompareAnchored
;
2668 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2669 flags
|= kCFCompareBackwards
;
2671 if (CFDictionaryGetValueIfPresent(query
, kSecMatchCaseInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2672 flags
|= kCFCompareCaseInsensitive
;
2674 if (CFDictionaryGetValueIfPresent(query
, kSecMatchDiacriticInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2675 flags
|= kCFCompareDiacriticInsensitive
;
2677 if (CFDictionaryGetValueIfPresent(query
, kSecMatchWidthInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2678 flags
|= kCFCompareWidthInsensitive
;
2684 _CssmKeyUsageFromQuery(CFDictionaryRef query
)
2687 uint32 keyUsage
= 0;
2688 if (!query
) return keyUsage
;
2690 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanEncrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2691 keyUsage
|= CSSM_KEYUSE_ENCRYPT
;
2693 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDecrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2694 keyUsage
|= CSSM_KEYUSE_DECRYPT
;
2696 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanSign
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2697 keyUsage
|= CSSM_KEYUSE_SIGN
;
2699 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanVerify
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2700 keyUsage
|= CSSM_KEYUSE_VERIFY
;
2702 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanWrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2703 keyUsage
|= CSSM_KEYUSE_WRAP
;
2705 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanUnwrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2706 keyUsage
|= CSSM_KEYUSE_UNWRAP
;
2708 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDerive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2709 keyUsage
|= CSSM_KEYUSE_DERIVE
;
2715 _ConvertItemClass(const void* item
, const void* keyClass
, Boolean
*isIdentity
)
2717 SecItemClass itemClass
= (SecItemClass
) 0;
2718 if (isIdentity
) *isIdentity
= false;
2720 if (CFEqual(item
, kSecClassGenericPassword
)) {
2721 itemClass
= kSecGenericPasswordItemClass
;
2723 else if (CFEqual(item
, kSecClassInternetPassword
)) {
2724 itemClass
= kSecInternetPasswordItemClass
;
2726 else if (CFEqual(item
, kSecClassCertificate
)) {
2727 itemClass
= kSecCertificateItemClass
;
2729 else if (CFEqual(item
, kSecClassIdentity
)) {
2730 // will perform a certificate lookup
2731 itemClass
= kSecCertificateItemClass
;
2732 if (isIdentity
) *isIdentity
= true;
2734 else if (CFEqual(item
, kSecClassKey
)) {
2735 // examine second parameter to determine type of key
2736 if (!keyClass
|| CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
2737 itemClass
= kSecSymmetricKeyItemClass
;
2739 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPublic
)) {
2740 itemClass
= kSecPublicKeyItemClass
;
2742 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
2743 itemClass
= kSecPrivateKeyItemClass
;
2751 _ItemClassFromItemList(CFArrayRef itemList
)
2753 // Given a list of items (standard or persistent references),
2754 // determine whether they all have the same item class. Returns
2755 // the item class, or 0 if multiple classes in list.
2756 SecItemClass result
= (SecItemClass
) 0;
2757 CFIndex index
, count
= (itemList
) ? CFArrayGetCount(itemList
) : 0;
2758 for (index
=0; index
< count
; index
++) {
2759 CFTypeRef item
= (CFTypeRef
) CFArrayGetValueAtIndex(itemList
, index
);
2761 SecKeychainItemRef itemRef
= NULL
;
2763 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
2764 // persistent reference, resolve first
2765 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemRef
);
2768 itemRef
= (SecKeychainItemRef
) CFRetain(item
);
2771 SecItemClass itemClass
= (SecItemClass
) 0;
2772 CFTypeID itemTypeID
= CFGetTypeID(itemRef
);
2773 if (itemTypeID
== SecIdentityGetTypeID() || itemTypeID
== SecCertificateGetTypeID()) {
2774 // Identities and certificates have the same underlying item class
2775 itemClass
= kSecCertificateItemClass
;
2777 else if (itemTypeID
== SecKeychainItemGetTypeID()) {
2778 // Reference to item in a keychain
2779 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2781 else if (itemTypeID
== SecKeyGetTypeID()) {
2782 // SecKey that isn't stored in a keychain
2783 // %%% will need to change this code when SecKey is no longer CSSM-based %%%
2784 const CSSM_KEY
*cssmKey
;
2785 status
= SecKeyGetCSSMKey((SecKeyRef
)itemRef
, &cssmKey
);
2786 if (status
== errSecSuccess
) {
2787 if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PUBLIC_KEY
)
2788 itemClass
= kSecPublicKeyItemClass
;
2789 else if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PRIVATE_KEY
)
2790 itemClass
= kSecPrivateKeyItemClass
;
2792 itemClass
= kSecSymmetricKeyItemClass
;
2796 if (itemClass
!= 0) {
2797 if (result
!= 0 && result
!= itemClass
) {
2798 return (SecItemClass
) 0; // different item classes in list; bail out
2808 // SecItemParams contains a validated set of input parameters, as well as a
2809 // search reference and attribute list built from those parameters. It is
2810 // designed to be allocated with _CreateSecItemParamsFromDictionary, and
2811 // freed with _FreeSecItemParams.
2813 struct SecItemParams
{
2814 CFDictionaryRef query
; // caller-supplied query
2815 int numResultTypes
; // number of result types requested
2816 int maxMatches
; // max number of matches to return
2817 uint32 keyUsage
; // key usage(s) requested
2818 Boolean returningAttributes
; // true if returning attributes dictionary
2819 Boolean returningData
; // true if returning item's data
2820 Boolean returningRef
; // true if returning item reference
2821 Boolean returningPersistentRef
; // true if returing a persistent reference
2822 Boolean returnAllMatches
; // true if we should return all matches
2823 Boolean returnIdentity
; // true if we are returning a SecIdentityRef
2824 Boolean trustedOnly
; // true if we only return trusted certs
2825 Boolean issuerAndSNToMatch
; // true if both issuer and SN were provided
2826 SecItemClass itemClass
; // item class for this query
2827 SecPolicyRef policy
; // value for kSecMatchPolicy (may be NULL)
2828 SecKeychainRef keychain
; // value for kSecUseKeychain (may be NULL)
2829 CFArrayRef useItems
; // value for kSecUseItemList (may be NULL)
2830 CFArrayRef itemList
; // value for kSecMatchItemList (may be NULL)
2831 CFTypeRef searchList
; // value for kSecMatchSearchList (may be NULL)
2832 CFTypeRef matchLimit
; // value for kSecMatchLimit (may be NULL)
2833 CFTypeRef emailAddrToMatch
; // value for kSecMatchEmailAddressIfPresent (may be NULL)
2834 CFTypeRef validOnDate
; // value for kSecMatchValidOnDate (may be NULL)
2835 CFTypeRef keyClass
; // value for kSecAttrKeyClass (may be NULL)
2836 CFTypeRef service
; // value for kSecAttrService (may be NULL)
2837 CFTypeRef issuer
; // value for kSecAttrIssuer (may be NULL)
2838 CFTypeRef matchIssuers
; // value for kSecMatchIssuers (may be NULL)
2839 CFTypeRef serialNumber
; // value for kSecAttrSerialNumber (may be NULL)
2840 CFTypeRef search
; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2841 CFTypeRef assumedKeyClass
; // if no kSecAttrKeyClass provided, holds the current class we're searching for
2842 CFIndex itemListIndex
; // if no search reference but we have itemList, holds index of next item to return
2843 SecKeychainAttributeList
*attrList
; // attribute list for this query
2844 SecAccessRef access
; // access reference (for SecItemAdd only, not used to find items)
2845 CFDataRef itemData
; // item data (for SecItemAdd only, not used to find items)
2846 CFTypeRef itemRef
; // item reference (to find, add, update or delete, depending on context)
2847 SecIdentityRef identityRef
; // identity reference (input as kSecValueRef)
2848 CFDataRef itemPersistentRef
; // item persistent reference (to find, add, update or delete, depending on context)
2849 Boolean isPCSItem
; // true if this query is for a Protected Cloud Storage item
2853 _ValidateDictionaryEntry(CFDictionaryRef dict
, CFTypeRef key
, const void **value
, CFTypeID expectedTypeID
, CFTypeID altTypeID
)
2855 if (!dict
|| !key
|| !value
|| !expectedTypeID
)
2858 if (!CFDictionaryGetValueIfPresent(dict
, key
, value
)) {
2859 // value was not provided for this key (not an error!)
2862 else if (!(*value
)) {
2863 // provided value is NULL (also not an error!)
2864 return errSecSuccess
;
2867 CFTypeID actualTypeID
= CFGetTypeID(*value
);
2868 if (!((expectedTypeID
== actualTypeID
) || (altTypeID
&& altTypeID
== actualTypeID
))) {
2869 // provided value does not have the expected (or alternate) CF type ID
2870 if ((expectedTypeID
== SecKeychainItemGetTypeID()) &&
2871 (actualTypeID
== SecKeyGetTypeID() || actualTypeID
== SecCertificateGetTypeID())) {
2872 // provided value is a "floating" reference which is not yet in a keychain
2874 return errSecSuccess
;
2876 return errSecItemInvalidValue
;
2879 // provided value is OK; retain it
2883 return errSecSuccess
;
2887 _EnsureUserDefaultKeychainIsSearched(SecItemParams
*itemParams
)
2890 CFArrayRef tmpList
= (CFArrayRef
) itemParams
->searchList
;
2892 // search list exists; make it mutable
2893 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2896 // no search list; start with default list
2897 status
= SecKeychainCopySearchList(&tmpList
);
2898 if (!status
&& tmpList
) {
2899 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2903 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2907 SecKeychainRef userKeychain
= NULL
;
2908 status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2909 if (!status
&& userKeychain
) {
2910 if (!CFArrayContainsValue((CFArrayRef
)itemParams
->searchList
,
2911 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)itemParams
->searchList
)), userKeychain
)) {
2912 // user's default keychain isn't currently in the search list, so append it
2913 CFArrayAppendValue((CFMutableArrayRef
)itemParams
->searchList
, userKeychain
);
2915 CFRelease(userKeychain
);
2920 _EnsureUserDefaultKeychainIsTargeted(SecItemParams
*itemParams
)
2922 if (itemParams
->keychain
) {
2923 return; // keychain is already explicitly specified, assume it's correct
2925 SecKeychainRef userKeychain
= NULL
;
2926 OSStatus status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2927 if (!status
&& userKeychain
) {
2928 itemParams
->keychain
= userKeychain
;
2933 _FreeSecItemParams(SecItemParams
*itemParams
)
2938 if (itemParams
->query
) CFRelease(itemParams
->query
);
2939 if (itemParams
->policy
) CFRelease(itemParams
->policy
);
2940 if (itemParams
->keychain
) CFRelease(itemParams
->keychain
);
2941 if (itemParams
->useItems
) CFRelease(itemParams
->useItems
);
2942 if (itemParams
->itemList
) CFRelease(itemParams
->itemList
);
2943 if (itemParams
->searchList
) CFRelease(itemParams
->searchList
);
2944 if (itemParams
->matchLimit
) CFRelease(itemParams
->matchLimit
);
2945 if (itemParams
->emailAddrToMatch
) CFRelease(itemParams
->emailAddrToMatch
);
2946 if (itemParams
->validOnDate
) CFRelease(itemParams
->validOnDate
);
2947 if (itemParams
->keyClass
) CFRelease(itemParams
->keyClass
);
2948 if (itemParams
->service
) CFRelease(itemParams
->service
);
2949 if (itemParams
->issuer
) CFRelease(itemParams
->issuer
);
2950 if (itemParams
->matchIssuers
) CFRelease(itemParams
->matchIssuers
);
2951 if (itemParams
->serialNumber
) CFRelease(itemParams
->serialNumber
);
2952 if (itemParams
->search
) CFRelease(itemParams
->search
);
2953 if (itemParams
->access
) CFRelease(itemParams
->access
);
2954 if (itemParams
->itemData
) CFRelease(itemParams
->itemData
);
2955 if (itemParams
->itemRef
) CFRelease(itemParams
->itemRef
);
2956 if (itemParams
->identityRef
) CFRelease(itemParams
->identityRef
);
2957 if (itemParams
->itemPersistentRef
) CFRelease(itemParams
->itemPersistentRef
);
2959 _FreeAttrList(itemParams
->attrList
);
2964 static SecItemParams
*
2965 _CreateSecItemParamsFromDictionary(CFDictionaryRef dict
, OSStatus
*error
)
2968 CFTypeRef value
= NULL
;
2969 CFDictionaryRef policyDict
= NULL
;
2970 SecItemParams
*itemParams
= (SecItemParams
*)calloc(1, sizeof(struct SecItemParams
));
2972 require_action(itemParams
!= NULL
, error_exit
, status
= errSecAllocate
);
2973 require_action(dict
&& (CFDictionaryGetTypeID() == CFGetTypeID(dict
)), error_exit
, status
= errSecParam
);
2975 itemParams
->query
= (CFDictionaryRef
) CFRetain(dict
);
2977 // validate input search parameters
2978 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchPolicy
, (const void **)&itemParams
->policy
, SecPolicyGetTypeID(), NULL
), error_exit
);
2979 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchSearchList
, (const void **)&itemParams
->searchList
, CFArrayGetTypeID(), NULL
), error_exit
);
2980 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchItemList
, (const void **)&itemParams
->itemList
, CFArrayGetTypeID(), NULL
), error_exit
);
2981 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchEmailAddressIfPresent
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
2982 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchValidOnDate
, (const void **)&itemParams
->validOnDate
, CFDateGetTypeID(), CFNullGetTypeID()), error_exit
);
2983 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchLimit
, (const void **)&itemParams
->matchLimit
, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit
);
2985 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseItemList
, (const void **)&itemParams
->useItems
, CFArrayGetTypeID(), NULL
), error_exit
);
2986 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseKeychain
, (const void **)&itemParams
->keychain
, SecKeychainGetTypeID(), NULL
), error_exit
);
2988 // validate a subset of input attributes (used to create an appropriate search reference)
2989 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrIssuer
, (const void **)&itemParams
->issuer
, CFDataGetTypeID(), NULL
), error_exit
);
2990 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrSerialNumber
, (const void **)&itemParams
->serialNumber
, CFDataGetTypeID(), NULL
), error_exit
);
2991 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrService
, (const void **)&itemParams
->service
, CFStringGetTypeID(), NULL
), error_exit
);
2992 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrKeyClass
, (const void **)&itemParams
->keyClass
, CFStringGetTypeID(), NULL
), error_exit
);
2994 if (itemParams
->service
&& CFStringHasPrefix((CFStringRef
)itemParams
->service
, CFSTR("ProtectedCloudStorage"))) {
2995 itemParams
->isPCSItem
= true;
2996 if (!SecItemSynchronizable(dict
)) {
2997 _EnsureUserDefaultKeychainIsSearched(itemParams
); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
2998 _EnsureUserDefaultKeychainIsTargeted(itemParams
); // for SecItemAdd
3002 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
3003 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueData
, (const void **)&itemParams
->itemData
, CFDataGetTypeID(), CFStringGetTypeID()), error_exit
);
3004 if (itemParams
->itemData
&& CFGetTypeID(itemParams
->itemData
) == CFStringGetTypeID()) {
3005 /* If we got a string, convert it into a data object */
3006 CFStringRef string
= (CFStringRef
)itemParams
->itemData
;
3007 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(string
), kCFStringEncodingUTF8
) + 1;
3008 CFMutableDataRef data
= CFDataCreateMutable(NULL
, maxLength
);
3009 require_action(data
, error_exit
, status
= errSecAllocate
);
3011 CFDataSetLength(data
, maxLength
);
3013 if (!CFStringGetCString(string
, (char *)CFDataGetMutableBytePtr(data
), maxLength
, kCFStringEncodingUTF8
)) {
3015 status
= errSecAllocate
;
3019 CFDataSetLength(data
, strlen((const char *)CFDataGetBytePtr(data
))); /* dont include NUL in string */
3020 itemParams
->itemData
= data
;
3024 // validate item references
3025 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueRef
, (const void **)&itemParams
->itemRef
, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit
);
3026 if (itemParams
->itemRef
&& (CFGetTypeID(itemParams
->itemRef
) == SecIdentityGetTypeID())) {
3027 itemParams
->identityRef
= (SecIdentityRef
)itemParams
->itemRef
;
3028 itemParams
->itemRef
= NULL
;
3029 SecIdentityCopyCertificate(itemParams
->identityRef
, (SecCertificateRef
*)&itemParams
->itemRef
);
3031 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValuePersistentRef
, (const void **)&itemParams
->itemPersistentRef
, CFDataGetTypeID(), NULL
), error_exit
);
3032 if (itemParams
->itemRef
|| itemParams
->itemPersistentRef
) {
3033 // Caller is trying to add or find an item by reference.
3034 // The supported method for doing that is to provide a kSecUseItemList array
3035 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
3036 // so add the item reference to those arrays here.
3037 if (itemParams
->useItems
) {
3038 CFArrayRef tmpItems
= itemParams
->useItems
;
3039 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3040 CFRelease(tmpItems
);
3042 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3044 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemRef
);
3045 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemPersistentRef
);
3047 if (itemParams
->itemList
) {
3048 CFArrayRef tmpItems
= itemParams
->itemList
;
3049 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3050 CFRelease(tmpItems
);
3052 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3054 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemRef
);
3055 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemPersistentRef
);
3058 // must have an explicit item class, unless one of the following is true:
3059 // - we have an item list to add or search (kSecUseItemList)
3060 // - we have an item reference or persistent reference for the thing we want to look up
3061 // Note that both of these cases will set itemParams->useItems.
3062 // If we have an item list to match (kSecMatchItemList), that still requires an item class,
3063 // so we can perform a search and see if the results match items in the list.
3065 if (!CFDictionaryGetValueIfPresent(dict
, kSecClass
, (const void**) &value
) && !itemParams
->useItems
) {
3066 require_action(false, error_exit
, status
= errSecItemClassMissing
);
3069 itemParams
->itemClass
= _ConvertItemClass(value
, itemParams
->keyClass
, &itemParams
->returnIdentity
);
3070 if (itemParams
->itemClass
== kSecSymmetricKeyItemClass
&& !itemParams
->keyClass
) {
3071 // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
3072 itemParams
->itemClass
= kSecSymmetricKeyItemClass
;
3073 itemParams
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3075 require_action(!(itemParams
->itemClass
== 0 && !itemParams
->useItems
), error_exit
, status
= errSecItemClassMissing
);
3078 // kSecMatchIssuers is only permitted with identities.
3079 // Convert the input issuers to normalized form.
3080 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchIssuers
, (const void **)&itemParams
->matchIssuers
, CFArrayGetTypeID(), NULL
), error_exit
);
3081 if (itemParams
->matchIssuers
) {
3082 require_action(itemParams
->returnIdentity
, error_exit
, status
= errSecParam
);
3083 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3084 CFArrayRef issuers
= (CFArrayRef
)itemParams
->matchIssuers
;
3085 if (canonical_issuers
) {
3086 CFIndex i
, count
= CFArrayGetCount(issuers
);
3087 for (i
= 0; i
< count
; i
++) {
3088 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(issuers
, i
);
3089 CFDataRef issuer_canonical
= NULL
;
3090 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
3091 issuer_canonical
= SecDistinguishedNameCopyNormalizedSequence((CFDataRef
)issuer_data
);
3092 if (issuer_canonical
) {
3093 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
3094 CFRelease(issuer_canonical
);
3097 if (CFArrayGetCount(canonical_issuers
) > 0) {
3098 CFAssignRetained(itemParams
->matchIssuers
, canonical_issuers
);
3100 CFRelease(canonical_issuers
);
3104 itemParams
->keyUsage
= _CssmKeyUsageFromQuery(dict
);
3105 itemParams
->trustedOnly
= CFDictionaryGetValueIfPresent(dict
, kSecMatchTrustedOnly
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3106 itemParams
->issuerAndSNToMatch
= (itemParams
->issuer
!= NULL
&& itemParams
->serialNumber
!= NULL
);
3108 // other input attributes, used for SecItemAdd but not for finding items
3109 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrAccess
, (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3110 if (itemParams
->access
== NULL
) {
3111 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3112 require_noerr(status
= _ValidateDictionaryEntry(dict
, CFSTR("kSecAttrAccess"), (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3115 // determine how to return the result
3116 itemParams
->numResultTypes
= 0;
3117 itemParams
->returningRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3118 if (itemParams
->returningRef
) ++itemParams
->numResultTypes
;
3119 itemParams
->returningPersistentRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnPersistentRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3120 if (itemParams
->returningPersistentRef
) ++itemParams
->numResultTypes
;
3121 itemParams
->returningAttributes
= CFDictionaryGetValueIfPresent(dict
, kSecReturnAttributes
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3122 if (itemParams
->returningAttributes
) ++itemParams
->numResultTypes
;
3123 itemParams
->returningData
= CFDictionaryGetValueIfPresent(dict
, kSecReturnData
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3124 if (itemParams
->returningData
) ++itemParams
->numResultTypes
;
3126 // default is kSecReturnRef if no result types were specified
3127 if (!itemParams
->numResultTypes
) {
3128 itemParams
->returningRef
= TRUE
;
3129 itemParams
->numResultTypes
= 1;
3132 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3133 itemParams
->maxMatches
= 1;
3134 itemParams
->returnAllMatches
= FALSE
;
3135 if (itemParams
->matchLimit
) {
3136 if (CFStringGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3137 itemParams
->returnAllMatches
= CFEqual(kSecMatchLimitAll
, itemParams
->matchLimit
);
3139 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3140 CFNumberGetValue((CFNumberRef
)itemParams
->matchLimit
, kCFNumberIntType
, &itemParams
->maxMatches
);
3141 require_action(!(itemParams
->maxMatches
< 0), error_exit
, status
= errSecMatchLimitUnsupported
);
3144 if (itemParams
->returnAllMatches
) {
3145 itemParams
->maxMatches
= INT32_MAX
;
3146 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3147 if ((itemParams
->itemClass
==kSecInternetPasswordItemClass
|| itemParams
->itemClass
==kSecGenericPasswordItemClass
) && itemParams
->returningData
)
3148 status
= errSecReturnDataUnsupported
;
3149 require_noerr(status
, error_exit
);
3152 // if we already have an item list (to add or find items in), we don't need a search reference
3153 if (itemParams
->useItems
) {
3154 if (itemParams
->itemClass
== 0) {
3155 itemParams
->itemClass
= _ItemClassFromItemList(itemParams
->useItems
);
3158 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3159 if (itemParams
->itemClass
!= 0) {
3160 status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
);
3162 status
= errSecSuccess
;
3164 goto error_exit
; // all done here
3167 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3168 require_noerr(status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
), error_exit
);
3170 // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
3171 if(itemParams
->policy
) {
3172 policyDict
= SecPolicyCopyProperties(itemParams
->policy
);
3173 CFStringRef oidStr
= (CFStringRef
) CFDictionaryGetValue(policyDict
, kSecPolicyOid
);
3174 if(oidStr
&& CFStringCompare(kSecPolicyAppleSMIME
,oidStr
,0) == 0) {
3175 require_noerr(status
= _ValidateDictionaryEntry(policyDict
, kSecPolicyName
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
3177 CFReleaseNull(policyDict
);
3180 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3181 if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->emailAddrToMatch
) {
3182 // searching for certificates by email address
3183 char *nameBuf
= (char*)malloc(MAXPATHLEN
);
3185 status
= errSecAllocate
;
3187 else if (CFStringGetCString((CFStringRef
)itemParams
->emailAddrToMatch
, nameBuf
, (CFIndex
)MAXPATHLEN
-1, kCFStringEncodingUTF8
)) {
3188 status
= SecKeychainSearchCreateForCertificateByEmail(itemParams
->searchList
, (const char *)nameBuf
, (SecKeychainSearchRef
*)&itemParams
->search
);
3191 status
= errSecItemInvalidValue
;
3193 if (nameBuf
) free(nameBuf
);
3195 else if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->issuerAndSNToMatch
) {
3196 // searching for certificates by issuer and serial number
3197 status
= SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams
->searchList
,
3198 (CFDataRef
)itemParams
->issuer
,
3199 (CFDataRef
)itemParams
->serialNumber
,
3200 (SecKeychainSearchRef
*)&itemParams
->search
);
3202 else if (itemParams
->returnIdentity
&& itemParams
->policy
) {
3203 // searching for identities by policy
3204 status
= SecIdentitySearchCreateWithPolicy(itemParams
->policy
,
3205 (CFStringRef
)itemParams
->service
,
3206 itemParams
->keyUsage
,
3207 itemParams
->searchList
,
3208 itemParams
->trustedOnly
,
3209 (SecIdentitySearchRef
*)&itemParams
->search
);
3211 else if (itemParams
->returnIdentity
) {
3212 // searching for identities
3213 status
= SecIdentitySearchCreate(itemParams
->searchList
,
3214 itemParams
->keyUsage
,
3215 (SecIdentitySearchRef
*)&itemParams
->search
);
3218 // normal keychain item search
3219 status
= SecKeychainSearchCreateFromAttributes(itemParams
->searchList
,
3220 itemParams
->itemClass
,
3221 (itemParams
->attrList
->count
== 0) ? NULL
: itemParams
->attrList
,
3222 (SecKeychainSearchRef
*)&itemParams
->search
);
3226 CFReleaseNull(policyDict
);
3228 _FreeSecItemParams(itemParams
);
3241 SecKeychainRef keychainRef
,
3242 SecAccessRef accessRef
,
3243 SecKeychainAttributeList
*attrList
,
3244 SecKeychainItemRef
*outItemRef
)
3248 // We must specify the access, since a free-floating key won't have one yet by default
3249 SecPointer
<Access
> access
;
3251 access
= Access::required(accessRef
);
3254 CFStringRef descriptor
= NULL
;
3256 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
3257 SecKeychainAttribute attr
= attrList
->attr
[index
];
3258 if (attr
.tag
== kSecKeyPrintName
) {
3259 descriptor
= CFStringCreateWithBytes(NULL
, (const UInt8
*)attr
.data
, attr
.length
, kCFStringEncodingUTF8
, FALSE
);
3264 if (descriptor
== NULL
) {
3265 descriptor
= (CFStringRef
) CFRetain(CFSTR("<unknown>"));
3267 access
= new Access(cfString(descriptor
));
3268 CFRelease(descriptor
);
3271 KeyItem
*key
= KeyItem::required(keyRef
);
3272 Item item
= key
->importTo(Keychain::optional(keychainRef
), access
, attrList
);
3274 *outItemRef
= item
->handle();
3280 _FilterWithPolicy(SecPolicyRef policy
, CFDateRef date
, SecCertificateRef cert
)
3282 CFDictionaryRef props
= NULL
;
3283 CFArrayRef keychains
= NULL
;
3284 CFArrayRef anchors
= NULL
;
3285 CFArrayRef certs
= NULL
;
3286 CFArrayRef chain
= NULL
;
3287 SecTrustRef trust
= NULL
;
3289 SecTrustResultType trustResult
;
3290 Boolean needChain
= false;
3292 if (!policy
|| !cert
) return errSecParam
;
3294 certs
= CFArrayCreate(NULL
, (const void **)&cert
, (CFIndex
)1, &kCFTypeArrayCallBacks
);
3295 status
= SecTrustCreateWithCertificates(certs
, policy
, &trust
);
3296 if(status
) goto cleanup
;
3298 /* Set evaluation date, if specified (otherwise current date is implied) */
3299 if (date
&& (CFGetTypeID(date
) == CFDateGetTypeID())) {
3300 status
= SecTrustSetVerifyDate(trust
, date
);
3301 if(status
) goto cleanup
;
3304 /* Check whether this is the X509 Basic policy, which means chain building */
3305 props
= SecPolicyCopyProperties(policy
);
3307 CFTypeRef oid
= (CFTypeRef
) CFDictionaryGetValue(props
, kSecPolicyOid
);
3308 if (oid
&& (CFEqual(oid
, kSecPolicyAppleX509Basic
) ||
3309 CFEqual(oid
, kSecPolicyAppleRevocation
))) {
3315 status
= SecTrustEvaluateLeafOnly(trust
, &trustResult
);
3317 status
= SecTrustEvaluate(trust
, &trustResult
);
3320 if (!(trustResult
== kSecTrustResultProceed
||
3321 trustResult
== kSecTrustResultUnspecified
||
3322 trustResult
== kSecTrustResultRecoverableTrustFailure
)) {
3323 /* The evaluation failed in a non-recoverable way */
3324 status
= errSecCertificateCannotOperate
;
3328 /* If there are no per-cert policy status codes,
3329 * and the cert has not expired, consider it valid for the policy.
3332 (void)SecTrustGetCssmResultCode(trust
, &status
);
3336 if(props
) CFRelease(props
);
3337 if(chain
) CFRelease(chain
);
3338 if(anchors
) CFRelease(anchors
);
3339 if(keychains
) CFRelease(keychains
);
3340 if(certs
) CFRelease(certs
);
3341 if(trust
) CFRelease(trust
);
3347 _FilterWithDate(CFTypeRef validOnDate
, SecCertificateRef cert
)
3349 if (!validOnDate
|| !cert
) return errSecParam
;
3351 CFAbsoluteTime at
, nb
, na
;
3352 if (CFGetTypeID(validOnDate
) == CFDateGetTypeID())
3353 at
= CFDateGetAbsoluteTime((CFDateRef
)validOnDate
);
3355 at
= CFAbsoluteTimeGetCurrent();
3357 OSStatus status
= errSecSuccess
;
3358 nb
= SecCertificateNotValidBefore(cert
);
3359 na
= SecCertificateNotValidAfter(cert
);
3361 if (nb
== 0 || na
== 0 || nb
== na
)
3362 status
= errSecCertificateCannotOperate
;
3364 status
= errSecCertificateNotValidYet
;
3366 status
= errSecCertificateExpired
;
3372 _FilterWithTrust(Boolean trustedOnly
, SecCertificateRef cert
)
3374 if (!cert
) return errSecParam
;
3375 if (!trustedOnly
) return errSecSuccess
;
3377 CFArrayRef certArray
= CFArrayCreate(NULL
, (const void**)&cert
, 1, &kCFTypeArrayCallBacks
);
3378 SecPolicyRef policy
= SecPolicyCreateWithOID(kSecPolicyAppleX509Basic
);
3379 OSStatus status
= (policy
== NULL
) ? errSecPolicyNotFound
: errSecSuccess
;
3382 SecTrustRef trust
= NULL
;
3383 status
= SecTrustCreateWithCertificates(certArray
, policy
, &trust
);
3385 SecTrustResultType trustResult
;
3386 status
= SecTrustEvaluate(trust
, &trustResult
);
3388 if (!(trustResult
== kSecTrustResultProceed
|| trustResult
== kSecTrustResultUnspecified
)) {
3389 status
= (trustResult
== kSecTrustResultDeny
) ? errSecTrustSettingDeny
: errSecNotTrusted
;
3397 CFRelease(certArray
);
3403 static bool items_matching_issuer_parent(CFDataRef issuer
, CFArrayRef issuers
, int recurse
) {
3404 if (!issuers
|| CFArrayGetCount(issuers
) == 0) { return false; }
3406 /* We found a match, we're done. */
3407 if (CFArrayContainsValue(issuers
, CFRangeMake(0, CFArrayGetCount(issuers
)), issuer
)) { return true; }
3409 /* Prevent infinite recursion */
3410 if (recurse
<= 0) { return false; }
3413 /* Query for parents */
3414 CFMutableDictionaryRef query
= NULL
;
3415 CFTypeRef parents
= NULL
;
3418 require_quiet(query
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 4, &kCFTypeDictionaryKeyCallBacks
,
3419 &kCFTypeDictionaryValueCallBacks
), out
);
3420 CFDictionaryAddValue(query
, kSecClass
, kSecClassCertificate
);
3421 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
3422 CFDictionaryAddValue(query
, kSecAttrSubject
, issuer
);
3423 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
3424 require_noerr_quiet(SecItemCopyMatching(query
, &parents
), out
);
3426 if (parents
&& CFArrayGetTypeID() == CFGetTypeID(parents
)) {
3427 CFIndex i
, count
= CFArrayGetCount((CFArrayRef
)parents
);
3428 for (i
= 0; i
< count
; i
++) {
3429 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex((CFArrayRef
)parents
, i
);
3430 CFDataRef cert_issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3431 if (CFEqual(cert_issuer
, issuer
)) {
3432 // Self-issued cert, don't look for parents.
3433 CFReleaseNull(cert_issuer
);
3436 found
= items_matching_issuer_parent(cert_issuer
, issuers
, recurse
);
3437 CFReleaseNull(cert_issuer
);
3438 if (found
) { break; }
3440 } else if (parents
&& SecCertificateGetTypeID() == CFGetTypeID(parents
)) {
3441 SecCertificateRef cert
= (SecCertificateRef
)parents
;
3442 CFDataRef cert_issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3443 require_action_quiet(!CFEqual(cert_issuer
, issuer
), out
, CFReleaseNull(cert_issuer
));
3444 found
= items_matching_issuer_parent(cert_issuer
, issuers
, recurse
);
3445 CFReleaseNull(cert_issuer
);
3449 CFReleaseNull(query
);
3450 CFReleaseNull(parents
);
3455 _FilterWithIssuers(CFArrayRef issuers
, SecCertificateRef cert
)
3457 if (!issuers
|| CFArrayGetCount(issuers
) == 0) return errSecParam
;
3458 if (!cert
) return errSecParam
;
3460 OSStatus status
= errSecInternalError
;
3462 /* kSecMatchIssuers matches certificates where ANY certificate in the chain has this issuer.
3463 * So we now need to recursively query the keychain for this cert's parents to determine if
3464 * they match. (This is why we limited the use of this key in _CreateSecItemParamsFromDictionary.) */
3465 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3466 if (items_matching_issuer_parent(issuer
, issuers
, 10)) {
3467 status
= errSecSuccess
;
3470 CFReleaseNull(issuer
);
3474 static SecKeychainItemRef
3475 CopyResolvedKeychainItem(CFTypeRef item
)
3477 SecKeychainItemRef kcItem
= NULL
;
3478 OSStatus status
= errSecSuccess
;
3480 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
3481 // persistent reference, resolve first
3482 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &kcItem
);
3486 kcItem
= (SecKeychainItemRef
) CFRetain(item
);
3489 // ask for the item's class:
3490 // will return an error if the item has been deleted
3491 SecItemClass itemClass
;
3492 SecCertificateRef certRef
= NULL
;
3493 CFTypeID itemTypeID
= CFGetTypeID(kcItem
);
3494 if (itemTypeID
== SecIdentityGetTypeID()) {
3495 status
= SecIdentityCopyCertificate((SecIdentityRef
)kcItem
, &certRef
);
3497 else if (itemTypeID
== SecCertificateGetTypeID()) {
3498 certRef
= (SecCertificateRef
) CFRetain(kcItem
);
3501 // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef
3502 itemClass
= kSecCertificateItemClass
;
3505 status
= SecKeychainItemCopyAttributesAndData(kcItem
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3520 UpdateKeychainSearchAndCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3522 // This function refreshes the search parameters in the specific case where
3523 // the caller is searching for kSecClassKey items but did not provide the
3524 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3525 // we must perform separate searches to obtain all results.
3527 OSStatus status
= errSecItemNotFound
;
3528 if (!params
|| !params
->assumedKeyClass
|| !params
->query
|| !item
)
3531 // Free the previous search reference and attribute list.
3533 CFRelease(params
->search
);
3534 params
->search
= NULL
;
3535 _FreeAttrList(params
->attrList
);
3536 params
->attrList
= NULL
;
3538 // Make a copy of the query dictionary so we can set the key class parameter.
3539 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(NULL
, 0, params
->query
);
3540 CFRelease(params
->query
);
3541 params
->query
= dict
;
3542 CFDictionarySetValue(dict
, kSecAttrKeyClass
, params
->assumedKeyClass
);
3544 // Determine the current item class for this search, and the next assumed key class.
3545 if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassSymmetric
)) {
3546 params
->itemClass
= kSecSymmetricKeyItemClass
;
3547 params
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3548 } else if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassPublic
)) {
3549 params
->itemClass
= kSecPublicKeyItemClass
;
3550 params
->assumedKeyClass
= kSecAttrKeyClassPrivate
;
3552 params
->itemClass
= kSecPrivateKeyItemClass
;
3553 params
->assumedKeyClass
= NULL
;
3556 // Rebuild the attribute list for the new key class.
3557 if (_CreateSecKeychainAttributeListFromDictionary(dict
, params
->itemClass
, ¶ms
->attrList
) == errSecSuccess
) {
3558 // Create a new search reference for the new attribute list.
3559 if (SecKeychainSearchCreateFromAttributes(params
->searchList
,
3561 (params
->attrList
->count
== 0) ? NULL
: params
->attrList
,
3562 (SecKeychainSearchRef
*)¶ms
->search
) == errSecSuccess
) {
3563 // Return the first matching item from the new search.
3564 // We won't come back here again until there are no more matching items for this search.
3565 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)params
->search
, (SecKeychainItemRef
*)item
);
3573 SecItemSearchCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3575 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3576 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3577 // depending on the type of search reference.
3580 CFTypeRef search
= (params
) ? params
->search
: NULL
;
3581 CFTypeID typeID
= (search
) ? CFGetTypeID(search
) : 0;
3583 if (search
&& typeID
== SecIdentitySearchGetTypeID()) {
3584 status
= SecIdentitySearchCopyNext((SecIdentitySearchRef
)search
, (SecIdentityRef
*)item
);
3586 else if (search
&& typeID
== SecKeychainSearchGetTypeID()) {
3587 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)search
, (SecKeychainItemRef
*)item
);
3588 // Check if we need to refresh the search for the next key class
3589 while (status
== errSecItemNotFound
&& params
->assumedKeyClass
!= NULL
)
3590 status
= UpdateKeychainSearchAndCopyNext(params
, item
);
3592 else if (typeID
== 0 && params
&& (params
->useItems
|| params
->itemList
)) {
3593 // No search available, but there is an item list available.
3594 // Return the next candidate item from the caller's item list
3595 CFArrayRef itemList
= (params
->useItems
) ? params
->useItems
: params
->itemList
;
3596 CFIndex count
= CFArrayGetCount(itemList
);
3597 *item
= (CFTypeRef
) NULL
;
3598 if (params
->itemListIndex
< count
) {
3599 *item
= (CFTypeRef
)CFArrayGetValueAtIndex(itemList
, params
->itemListIndex
++);
3601 // Potentially resolve persistent item references here, and
3602 // verify the item reference we're about to hand back is still
3603 // valid (it could have been deleted from the keychain while
3604 // our query was holding onto the itemList).
3605 *item
= CopyResolvedKeychainItem(*item
);
3606 if (*item
&& (CFGetTypeID(*item
) == SecIdentityGetTypeID())) {
3607 // Persistent reference resolved to an identity, so return that type.
3608 params
->returnIdentity
= true;
3612 status
= (*item
) ? errSecSuccess
: errSecItemNotFound
;
3615 status
= errSecItemNotFound
;
3621 FilterCandidateItem(CFTypeRef
*item
, SecItemParams
*itemParams
, SecIdentityRef
*identity
)
3623 if (!item
|| *item
== NULL
|| !itemParams
)
3624 return errSecItemNotFound
;
3627 CFStringRef commonName
= NULL
;
3628 SecIdentityRef foundIdentity
= NULL
;
3629 if (CFGetTypeID(*item
) == SecIdentityGetTypeID()) {
3630 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3631 // replace the found "item" with its associated certificate (which is the
3632 // item we actually want for purposes of getting attributes, data, or a
3633 // persistent data reference), and return the identity separately.
3634 SecCertificateRef certificate
;
3635 status
= SecIdentityCopyCertificate((SecIdentityRef
) *item
, &certificate
);
3636 if (itemParams
->returnIdentity
) {
3637 foundIdentity
= (SecIdentityRef
) *item
;
3639 *identity
= foundIdentity
;
3645 *item
= (CFTypeRef
)certificate
;
3648 CFDictionaryRef query
= itemParams
->query
;
3650 if (itemParams
->itemClass
== kSecCertificateItemClass
) {
3651 // perform string comparisons first
3652 CFStringCompareFlags flags
= _StringCompareFlagsFromQuery(query
);
3653 CFStringRef nameContains
, nameStarts
, nameEnds
, nameExact
;
3654 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectContains
, (const void **)&nameContains
))
3655 nameContains
= NULL
;
3656 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&nameStarts
))
3658 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&nameEnds
))
3660 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectWholeString
, (const void **)&nameExact
))
3662 if (nameContains
|| nameStarts
|| nameEnds
|| nameExact
) {
3663 status
= SecCertificateCopyCommonName((SecCertificateRef
)*item
, &commonName
);
3664 if (status
|| !commonName
) goto filterOut
;
3667 CFRange range
= CFStringFind(commonName
, nameContains
, flags
);
3668 if (range
.length
< 1)
3670 // certificate item contains string; proceed to next check
3673 CFRange range
= CFStringFind(commonName
, nameStarts
, flags
);
3674 if (range
.length
< 1 || range
.location
> 1)
3676 // certificate item starts with string; proceed to next check
3679 CFRange range
= CFStringFind(commonName
, nameEnds
, flags
);
3680 if (range
.length
< 1 || range
.location
!= (CFStringGetLength(commonName
) - CFStringGetLength(nameEnds
)))
3682 // certificate item ends with string; proceed to next check
3685 CFRange range
= CFStringFind(commonName
, nameExact
, flags
);
3686 if (range
.length
< 1 || (CFStringGetLength(commonName
) != CFStringGetLength(nameExact
)))
3688 // certificate item exactly matches string; proceed to next check
3690 if (itemParams
->returnIdentity
) {
3691 // if we already found and returned the identity, we can skip this
3692 if (!foundIdentity
) {
3693 status
= SecIdentityCreateWithCertificate(itemParams
->searchList
, (SecCertificateRef
) *item
, identity
);
3694 if (status
) goto filterOut
;
3696 // certificate item is part of an identity; proceed to next check
3698 if (itemParams
->policy
) {
3699 status
= _FilterWithPolicy(itemParams
->policy
, (CFDateRef
)itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3700 if (status
) goto filterOut
;
3701 // certificate item is valid for specified policy (and optionally specified date)
3703 if (itemParams
->validOnDate
) {
3704 status
= _FilterWithDate(itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3705 if (status
) goto filterOut
;
3706 // certificate item is valid for specified date
3708 if (itemParams
->trustedOnly
) {
3709 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3710 // their trust has already been validated and we can skip this part.
3711 if (!(foundIdentity
&& itemParams
->returnIdentity
&& itemParams
->policy
)) {
3712 status
= _FilterWithTrust(itemParams
->trustedOnly
, (SecCertificateRef
) *item
);
3713 if (status
) goto filterOut
;
3715 // certificate item is trusted on this system
3717 if (itemParams
->matchIssuers
) {
3718 status
= _FilterWithIssuers((CFArrayRef
)itemParams
->matchIssuers
, (SecCertificateRef
) *item
);
3719 if (status
) goto filterOut
;
3720 // certificate item has one of the issuers
3723 if (itemParams
->itemList
) {
3724 Boolean foundMatch
= FALSE
;
3725 CFIndex idx
, count
= CFArrayGetCount(itemParams
->itemList
);
3726 for (idx
=0; idx
<count
; idx
++) {
3727 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->itemList
, idx
);
3728 SecKeychainItemRef realItem
= NULL
;
3729 SecCertificateRef aCert
= NULL
;
3730 if (anItem
== NULL
) {
3733 if (CFDataGetTypeID() == CFGetTypeID(anItem
) &&
3734 errSecSuccess
== SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
)) {
3737 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
) &&
3738 errSecSuccess
== SecIdentityCopyCertificate((SecIdentityRef
)anItem
, &aCert
)) {
3741 if (CFEqual(anItem
, (CFTypeRef
) *item
)) {
3748 CFRelease(realItem
);
3754 if (!foundMatch
) goto filterOut
;
3755 // item was found on provided list
3758 if (foundIdentity
&& !identity
) {
3759 CFRelease(foundIdentity
);
3762 CFRelease(commonName
);
3765 // if we get here, consider the item a match
3766 return errSecSuccess
;
3770 CFRelease(commonName
);
3774 if (foundIdentity
) {
3775 CFRelease(foundIdentity
);
3780 return errSecItemNotFound
;
3784 AddItemResults(SecKeychainItemRef item
,
3785 SecIdentityRef identity
,
3786 SecItemParams
*itemParams
,
3787 CFAllocatorRef allocator
,
3788 CFMutableArrayRef
*items
,
3791 // Given a found item (which may also be an identity), this function adds
3792 // the requested result types (specified in itemParams) to the appropriate
3793 // container as follows:
3795 // 1. If there is only one result type (numResultTypes == 1) and only one
3796 // match requested (maxMatches == 1), set *result directly.
3798 // 2. If there are multiple result types (numResultTypes > 1), and only one
3799 // match requested (maxMatches == 1), add each result type to itemDict
3800 // and set itemDict as the value of *result.
3802 // 3. If there is only one result type (numResultTypes == 1) and multiple
3803 // possible matches (maxMatches > 1), add the result type to *items
3804 // and set *items as the value of *result.
3806 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3807 // possible matches (maxMatches > 1), add each result type to itemDict,
3808 // add itemDict to *items, and set *items as the value of *result.
3810 // Note that we allocate *items if needed.
3812 if (!item
|| !itemParams
|| !result
)
3815 if (itemParams
->maxMatches
> 1) {
3816 // if we can return more than one item, we must have an array
3819 else if (*items
== NULL
)
3820 *items
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3823 OSStatus tmpStatus
, status
= errSecSuccess
;
3824 CFMutableArrayRef itemArray
= (items
) ? *items
: NULL
;
3825 CFMutableDictionaryRef itemDict
= NULL
;
3826 if (itemParams
->numResultTypes
> 1) {
3827 // if we're returning more than one result type, each item we return must be a dictionary
3828 itemDict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3831 if (itemParams
->returningRef
) {
3832 const void* itemRef
= (identity
) ? (const void*)identity
: (const void*)item
;
3834 CFDictionaryAddValue(itemDict
, kSecValueRef
, itemRef
);
3836 else if (itemArray
) {
3837 CFArrayAppendValue(itemArray
, itemRef
);
3840 *result
= CFRetain((CFTypeRef
)itemRef
);
3844 if (itemParams
->returningPersistentRef
) {
3845 CFDataRef persistentRef
;
3846 SecKeychainItemRef tmpItem
= item
;
3847 if (itemParams
->identityRef
) {
3848 tmpItem
= (SecKeychainItemRef
)itemParams
->identityRef
;
3850 tmpStatus
= SecKeychainItemCreatePersistentReference(tmpItem
, &persistentRef
);
3851 if (tmpStatus
== errSecSuccess
) {
3853 CFDictionaryAddValue(itemDict
, kSecValuePersistentRef
, persistentRef
);
3855 else if (itemArray
) {
3856 CFArrayAppendValue(itemArray
, persistentRef
);
3859 *result
= CFRetain(persistentRef
);
3861 CFRelease(persistentRef
);
3863 else if (status
== errSecSuccess
) {
3868 if (itemParams
->returningData
) {
3869 // Use SecCertificateCopyData if we have a SecCertificateRef item.
3870 // Note that a SecCertificateRef may not actually be a SecKeychainItem,
3871 // in which case SecKeychainItemCopyContent will not obtain its data.
3873 if (CFGetTypeID(item
) == SecCertificateGetTypeID()) {
3874 CFDataRef dataRef
= SecCertificateCopyData((SecCertificateRef
)item
);
3877 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3879 else if (itemArray
) {
3880 CFArrayAppendValue(itemArray
, dataRef
);
3883 *result
= CFRetain(dataRef
);
3886 status
= errSecSuccess
;
3889 status
= errSecAllocate
;
3895 tmpStatus
= SecKeychainItemCopyContent(item
, NULL
, NULL
, &length
, &data
);
3896 if (tmpStatus
== errSecSuccess
) {
3897 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)data
, length
);
3899 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3901 else if (itemArray
) {
3902 CFArrayAppendValue(itemArray
, dataRef
);
3905 *result
= CFRetain(dataRef
);
3908 (void) SecKeychainItemFreeContent(NULL
, data
);
3910 else if (status
== errSecSuccess
) {
3916 if (itemParams
->returningAttributes
) {
3917 CFDictionaryRef attrsDict
= NULL
;
3918 SecItemClass itemClass
;
3919 // since we have an item, allow its actual class to override the query-specified item class
3920 tmpStatus
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3922 itemClass
= itemParams
->itemClass
;
3924 tmpStatus
= _CreateAttributesDictionaryFromItem(allocator
, itemClass
, item
, &attrsDict
);
3927 // add all keys and values from attrsDict to the item dictionary
3928 CFDictionaryApplyFunction(attrsDict
, _AddDictValueToOtherDict
, &itemDict
);
3930 else if (itemArray
) {
3931 CFArrayAppendValue(itemArray
, attrsDict
);
3934 *result
= CFRetain(attrsDict
);
3936 CFRelease(attrsDict
);
3938 if (tmpStatus
&& (status
== errSecSuccess
)) {
3945 CFArrayAppendValue(itemArray
, itemDict
);
3946 CFRelease(itemDict
);
3947 *result
= itemArray
;
3953 else if (itemArray
) {
3954 *result
= itemArray
;
3960 CFDataRef
_SecItemGetPersistentReference(CFTypeRef raw_item
)
3963 Item item
= ItemImpl::required((SecKeychainItemRef
)raw_item
);
3964 return item
->getPersistentRef();
3970 /******************************************************************************/
3971 #pragma mark SecItem API functions
3972 /******************************************************************************/
3975 // Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3977 static SInt32
readNumber(CFTypeRef obj
) {
3978 CFTypeID tid
= CFGetTypeID(obj
);
3980 if (tid
== CFNumberGetTypeID()) {
3981 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt32Type
, &v
);
3983 } else if (tid
== CFBooleanGetTypeID()) {
3984 v
= CFBooleanGetValue((CFBooleanRef
)obj
);
3986 } else if (tid
== CFStringGetTypeID()) {
3987 v
= CFStringGetIntValue((CFStringRef
)obj
);
3988 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v
);
3989 /* If a string converted to an int isn't equal to the int printed as
3990 a string, return a CFStringRef instead. */
3991 if (!CFEqual(t
, obj
)) {
4002 // Function to check whether the kSecAttrSynchronizable flag is set in the query.
4004 static Boolean
SecItemSynchronizable(CFDictionaryRef query
)
4006 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
);
4007 Boolean result
= (value
&& readNumber(value
));
4013 // Function to check whether a synchronizable persistent reference was provided.
4015 static Boolean
SecItemIsIOSPersistentReference(CFTypeRef value
)
4018 return ::_SecItemParsePersistentRef((CFDataRef
)value
, NULL
, NULL
, NULL
);
4023 extern "C" Boolean
SecKeyIsCDSAKey(SecKeyRef ref
);
4026 // Function to find out which keychains are targetted by the query.
4028 static OSStatus
SecItemCategorizeQuery(CFDictionaryRef query
, bool &can_target_ios
, bool &can_target_osx
)
4030 // By default, target both keychain.
4031 can_target_osx
= can_target_ios
= true;
4033 // Check no-legacy flag.
4034 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrNoLegacy
);
4035 if (value
!= NULL
) {
4036 can_target_ios
= readNumber(value
) != 0;
4037 can_target_osx
= !can_target_ios
;
4038 return errSecSuccess
;
4041 // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value.
4042 value
= CFDictionaryGetValue(query
, kSecValueRef
);
4043 if (value
!= NULL
) {
4044 CFTypeID typeID
= CFGetTypeID(value
);
4045 if (typeID
== SecKeyGetTypeID()) {
4046 can_target_osx
= SecKeyIsCDSAKey((SecKeyRef
)value
);
4047 can_target_ios
= !can_target_osx
;
4048 } else if (typeID
== SecCertificateGetTypeID()) {
4049 // All types of certificates can target OSX keychains, but OSX certificates won't work on iOS
4050 can_target_ios
&= !SecCertificateIsItemImplInstance((SecCertificateRef
)value
);
4051 } else if (typeID
== SecKeychainItemGetTypeID()) {
4052 // SecKeychainItemRef can target iOS keychain only when it has attached iOS-style persistent reference.
4053 if (_SecItemGetPersistentReference(value
) == NULL
) {
4054 can_target_ios
= false;
4059 // Check presence of kSecAttrTokenID and kSecAttrAccessControl; they are not defined for CDSA keychain.
4060 if (CFDictionaryContainsKey(query
, kSecAttrTokenID
) || CFDictionaryContainsKey(query
, kSecAttrAccessControl
)) {
4061 can_target_osx
= false;
4064 // Check for special token access groups. If present, redirect query to iOS keychain.
4065 value
= CFDictionaryGetValue(query
, kSecAttrAccessGroup
);
4066 if (value
!= NULL
&& CFEqual(value
, kSecAttrAccessGroupToken
)) {
4067 can_target_osx
= false;
4070 // Synchronizable items should go to iOS keychain only.
4071 if (SecItemSynchronizable(query
)) {
4072 can_target_osx
= false;
4075 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
4076 if (value
!= NULL
) {
4077 if (SecItemIsIOSPersistentReference(value
)) {
4078 can_target_osx
= false;
4080 // Non-iOS-style persistent references should not be fed to iOS keychain queries.
4081 can_target_ios
= false;
4085 // Presence of following atributes means that query is OSX-only.
4086 static const CFStringRef
*osx_only_items
[] = {
4088 &kSecMatchSearchList
,
4089 &kSecMatchSubjectStartsWith
,
4090 &kSecMatchSubjectEndsWith
,
4091 &kSecMatchSubjectWholeString
,
4092 &kSecMatchDiacriticInsensitive
,
4093 &kSecMatchWidthInsensitive
,
4102 for (CFIndex i
= 0; i
< array_size(osx_only_items
); i
++) {
4103 can_target_ios
= can_target_ios
&& !CFDictionaryContainsKey(query
, *osx_only_items
[i
]);
4106 return (can_target_ios
|| can_target_osx
) ? errSecSuccess
: errSecParam
;
4110 // Function to check whether the kSecAttrSynchronizable attribute is being updated.
4112 static Boolean
SecItemHasSynchronizableUpdate(Boolean synchronizable
, CFDictionaryRef changes
)
4114 CFTypeRef newValue
= CFDictionaryGetValue(changes
, kSecAttrSynchronizable
);
4118 Boolean new_sync
= readNumber(newValue
);
4119 Boolean old_sync
= synchronizable
;
4121 return (old_sync
!= new_sync
);
4125 // Function to apply changes to a mutable dictionary.
4126 // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
4128 static void SecItemApplyChanges(const void *key
, const void *value
, void *context
)
4130 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
) context
;
4133 CFDictionarySetValue(dict
, key
, value
);
4137 // Function to change matching items from non-syncable to syncable
4138 // (if toSyncable is true), otherwise from syncable to non-syncable.
4139 // This currently moves items between keychain containers.
4141 static OSStatus
SecItemChangeSynchronizability(CFDictionaryRef query
, CFDictionaryRef changes
, Boolean toSyncable
)
4143 // Note: the input query dictionary is a mutable copy of the query originally
4144 // provided by the caller as the first parameter to SecItemUpdate. It may not
4145 // specify returning attributes or data, but we will need both to make a copy.
4147 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnRef
);
4148 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnPersistentRef
);
4149 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnData
);
4150 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnAttributes
, kCFBooleanTrue
);
4151 if (NULL
== CFDictionaryGetValue(changes
, kSecValueData
))
4152 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnData
, kCFBooleanTrue
);
4157 status
= SecItemCopyMatching_osx(query
, &result
);
4159 status
= SecItemCopyMatching_ios(query
, &result
);
4164 return errSecItemNotFound
;
4166 CFMutableArrayRef items
;
4167 if (CFGetTypeID(result
) != CFArrayGetTypeID()) {
4168 items
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4169 CFArrayAppendValue(items
, result
);
4173 items
= (CFMutableArrayRef
)result
;
4176 CFIndex idx
, count
= (items
) ? CFArrayGetCount(items
) : 0;
4177 int priority
= LOG_DEBUG
;
4179 for (idx
= 0; idx
< count
; idx
++) {
4180 CFDictionaryRef dict
= (CFDictionaryRef
) CFArrayGetValueAtIndex(items
, idx
);
4181 CFMutableDictionaryRef item
= (CFMutableDictionaryRef
)
4182 SecItemCopyTranslatedAttributes(dict
,
4183 CFDictionaryGetValue(query
, kSecClass
),
4184 (toSyncable
) ? true : false /*iOSOut*/,
4185 true /*pruneMatch*/,
4187 true /*pruneReturn*/,
4188 false /*pruneData*/,
4189 (toSyncable
) ? true : false /*pruneAccess*/);
4190 // hold onto the query before applying changes, in case the item already exists.
4191 // note that we cannot include the creation or modification dates from our
4192 // found item in this query, as they may not match the item in the other keychain.
4193 CFMutableDictionaryRef itemQuery
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
4194 CFDictionaryRemoveValue(itemQuery
, kSecAttrCreationDate
);
4195 CFDictionaryRemoveValue(itemQuery
, kSecAttrModificationDate
);
4196 // apply changes to the item dictionary that we will pass to SecItemAdd
4197 CFDictionaryApplyFunction(changes
, SecItemApplyChanges
, item
);
4199 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4200 status
= SecItemAdd_ios(item
, NULL
);
4201 secitemlog(priority
, "ChangeSync: SecItemAdd_ios=%d", status
);
4202 if (errSecDuplicateItem
== status
) {
4203 // find and apply changes to the existing syncable item.
4204 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4205 status
= SecItemUpdate_ios(itemQuery
, changes
);
4206 secitemlog(priority
, "ChangeSync: SecItemUpdate_ios=%d", status
);
4208 if (errSecSuccess
== status
) {
4209 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4210 status
= SecItemDelete_osx(itemQuery
);
4211 secitemlog(priority
, "ChangeSync: SecItemDelete_osx=%d", status
);
4215 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4216 status
= SecItemAdd_osx(item
, NULL
);
4217 secitemlog(priority
, "ChangeSync: SecItemAdd_osx=%d", status
);
4218 if (errSecDuplicateItem
== status
) {
4219 // find and apply changes to the existing non-syncable item.
4220 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4221 status
= SecItemUpdate_osx(itemQuery
, changes
);
4222 secitemlog(priority
, "ChangeSync: SecItemUpdate_osx=%d", status
);
4224 if (errSecSuccess
== status
) {
4225 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4226 status
= SecItemDelete_ios(itemQuery
);
4227 secitemlog(priority
, "ChangeSync: SecItemDelete_ios=%d", status
);
4230 CFReleaseSafe(item
);
4231 CFReleaseSafe(itemQuery
);
4235 CFReleaseSafe(items
);
4244 SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes
) {
4245 CFTypeRef ref
= NULL
;
4246 CFStringRef item_class_string
= (CFStringRef
)CFDictionaryGetValue(refAttributes
, kSecClass
);
4247 SecItemClass item_class
= (SecItemClass
) 0;
4249 if (CFEqual(item_class_string
, kSecClassGenericPassword
)) {
4250 item_class
= kSecGenericPasswordItemClass
;
4251 } else if (CFEqual(item_class_string
, kSecClassInternetPassword
)) {
4252 item_class
= kSecInternetPasswordItemClass
;
4255 if (item_class
!= 0) {
4256 // we carry v_Data around here so the *_ios calls can find it and locate
4257 // their own data. Putting things in the attribute list doesn't help as
4258 // the osx keychainitem and item calls bail when they don't see a keychain
4259 // object. If we need to make them work we either have to bridge them, or
4260 // find a way to craft a workable keychain object. #if'ed code left below
4261 // in case we need to go down that path.
4263 SecKeychainAttributeList attrs
= {};
4264 SecKeychainAttribute attr
= {};
4270 Item item
= Item(item_class
, &attrs
, 0, "");
4271 v
= CFDictionaryGetValue(refAttributes
, kSecValuePersistentRef
);
4273 item
->setPersistentRef((CFDataRef
)v
);
4275 ref
= item
->handle();
4282 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4283 * is a member of the specified application group, and is signed by Apple.
4286 SecItemValidateAppleApplicationGroupAccess(CFStringRef group
)
4288 SecTrustedApplicationRef app
= NULL
;
4289 SecRequirementRef requirement
= NULL
;
4290 SecCodeRef code
= NULL
;
4291 OSStatus status
= errSecParam
;
4294 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(group
), kCFStringEncodingUTF8
) + 1;
4295 char* buffer
= (char*) malloc(length
);
4297 if (CFStringGetCString(group
, buffer
, length
, kCFStringEncodingUTF8
)) {
4298 status
= SecTrustedApplicationCreateApplicationGroup(buffer
, NULL
, &app
);
4302 status
= errSecMemoryError
;
4306 status
= SecTrustedApplicationCopyRequirement(app
, &requirement
);
4309 status
= SecCodeCopySelf(kSecCSDefaultFlags
, &code
);
4312 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, requirement
);
4315 CFReleaseSafe(code
);
4316 CFReleaseSafe(requirement
);
4321 static Mutex gParentCertCacheLock
;
4322 static CFMutableDictionaryRef gParentCertCache
;
4323 static CFMutableArrayRef gParentCertCacheList
;
4324 #define PARENT_CACHE_SIZE 100
4326 void SecItemParentCachePurge() {
4327 StLock
<Mutex
> _(gParentCertCacheLock
);
4328 CFReleaseNull(gParentCertCache
);
4329 CFReleaseNull(gParentCertCacheList
);
4332 static CFArrayRef CF_RETURNS_RETAINED
parentCacheRead(SecCertificateRef certificate
) {
4333 CFArrayRef parents
= NULL
;
4335 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4336 if (!digest
) return NULL
;
4338 StLock
<Mutex
> _(gParentCertCacheLock
);
4339 if (gParentCertCache
&& gParentCertCacheList
) {
4340 if (0 <= (ix
= CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4341 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4343 // Cache hit. Get value and move entry to the top of the list.
4344 parents
= (CFArrayRef
)CFDictionaryGetValue(gParentCertCache
, digest
);
4345 CFArrayRemoveValueAtIndex(gParentCertCacheList
, ix
);
4346 CFArrayAppendValue(gParentCertCacheList
, digest
);
4349 CFRetainSafe(parents
);
4353 static void parentCacheWrite(SecCertificateRef certificate
, CFArrayRef parents
) {
4354 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
4355 if (!digest
) return;
4357 StLock
<Mutex
> _(gParentCertCacheLock
);
4358 if (!gParentCertCache
|| !gParentCertCacheList
) {
4359 CFReleaseNull(gParentCertCache
);
4360 gParentCertCache
= makeCFMutableDictionary();
4361 CFReleaseNull(gParentCertCacheList
);
4362 gParentCertCacheList
= makeCFMutableArray(0);
4365 if (gParentCertCache
&& gParentCertCacheList
) {
4366 // check to make sure another thread didn't add this entry to the cache already
4367 if (0 > CFArrayGetFirstIndexOfValue(gParentCertCacheList
,
4368 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList
)),
4370 CFDictionaryAddValue(gParentCertCache
, digest
, parents
);
4371 if (PARENT_CACHE_SIZE
<= CFArrayGetCount(gParentCertCacheList
)) {
4372 // Remove least recently used cache entry.
4373 CFArrayRemoveValueAtIndex(gParentCertCacheList
, 0);
4375 CFArrayAppendValue(gParentCertCacheList
, digest
);
4381 * SecItemCopyParentCertificates_osx returns an array of zero of more possible
4382 * issuer certificates for the provided certificate. No cryptographic validation
4383 * of the signature is performed in this function; its purpose is only to
4384 * provide a list of candidate certificates.
4387 SecItemCopyParentCertificates_osx(SecCertificateRef certificate
, void *context
)
4389 #pragma unused (context) /* for now; in future this can reference a container object */
4390 /* Check for parents in keychain cache */
4391 CFArrayRef parents
= parentCacheRead(certificate
);
4396 /* Cache miss. Query for parents. */
4397 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4398 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4400 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4401 CFRetainSafe(normalizedIssuer
);
4404 CFMutableArrayRef combinedSearchList
= NULL
;
4406 /* Define the array of keychains which will be searched for parents. */
4407 CFArrayRef searchList
= NULL
;
4408 status
= SecKeychainCopySearchList(&searchList
);
4410 combinedSearchList
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, searchList
);
4411 CFRelease(searchList
);
4413 combinedSearchList
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4415 SecKeychainRef rootStoreKeychain
= NULL
;
4416 status
= SecKeychainOpen(SYSTEM_ROOT_STORE_PATH
, &rootStoreKeychain
);
4417 if (rootStoreKeychain
) {
4418 if (combinedSearchList
) {
4419 CFArrayAppendValue(combinedSearchList
, rootStoreKeychain
);
4421 CFRelease(rootStoreKeychain
);
4424 /* Create and populate a fixed-size query dictionary. */
4425 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 5,
4426 &kCFTypeDictionaryKeyCallBacks
,
4427 &kCFTypeDictionaryValueCallBacks
);
4428 CFDictionaryAddValue(query
, kSecClass
, kSecClassCertificate
);
4429 CFDictionaryAddValue(query
, kSecReturnData
, kCFBooleanTrue
);
4430 CFDictionaryAddValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
4431 if (combinedSearchList
) {
4432 CFDictionaryAddValue(query
, kSecMatchSearchList
, combinedSearchList
);
4433 CFRelease(combinedSearchList
);
4436 CFTypeRef results
= NULL
;
4437 if (normalizedIssuer
) {
4438 /* Look up certs whose subject is the same as this cert's issuer. */
4439 CFDictionaryAddValue(query
, kSecAttrSubject
, normalizedIssuer
);
4440 status
= SecItemCopyMatching_osx(query
, &results
);
4443 /* Cannot match anything without an issuer! */
4444 status
= errSecItemNotFound
;
4447 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4448 secitemlog(LOG_WARNING
, "SecItemCopyParentCertificates_osx: %d", (int)status
);
4452 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4453 CFTypeID resultType
= (results
) ? CFGetTypeID(results
) : 0;
4454 if (resultType
== CFArrayGetTypeID()) {
4455 CFIndex index
, count
= CFArrayGetCount((CFArrayRef
)results
);
4456 for (index
= 0; index
< count
; index
++) {
4457 CFDataRef data
= (CFDataRef
) CFArrayGetValueAtIndex((CFArrayRef
)results
, index
);
4459 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
4461 CFArrayAppendValue(result
, cert
);
4466 } else if (results
&& resultType
== CFDataGetTypeID()) {
4467 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)results
);
4469 CFArrayAppendValue(result
, cert
);
4473 CFReleaseSafe(results
);
4474 CFReleaseSafe(normalizedIssuer
);
4477 parentCacheWrite(certificate
, result
);
4482 SecCertificateRef
SecItemCopyStoredCertificate(SecCertificateRef certificate
, void *context
)
4484 #pragma unused (context) /* for now; in future this can reference a container object */
4486 /* Certificates are unique by issuer and serial number. */
4487 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4488 CFDataRef serialNumber
= SecCertificateCopySerialNumber(certificate
, NULL
);
4489 CFDataRef normalizedIssuer
= SecCertificateCopyNormalizedIssuerContent(certificate
, NULL
);
4491 CFDataRef serialNumber
= SecCertificateCopySerialNumber(certificate
);
4492 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
4493 CFRetainSafe(normalizedIssuer
);
4496 const void *keys
[] = {
4500 kSecAttrSerialNumber
,
4504 kSecClassCertificate
,
4510 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 5, NULL
, NULL
);
4511 CFTypeRef result
= NULL
;
4513 OSStatus status
= SecItemCopyMatching_osx(query
, &result
);
4514 if ((status
!= errSecSuccess
) && (status
!= errSecItemNotFound
)) {
4515 secitemlog(LOG_WARNING
, "SecItemCopyStoredCertificate: %d", (int)status
);
4516 CFReleaseNull(result
);
4518 CFReleaseSafe(query
);
4519 CFReleaseSafe(serialNumber
);
4520 CFReleaseSafe(normalizedIssuer
);
4522 return (SecCertificateRef
)result
;
4526 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4527 * and attempts to return a sanitized copy for passing to the underlying
4528 * platform-specific implementation code.
4530 * If iOSOut is true, one or more translations may apply:
4531 * - SecKeychain refs are removed, since there aren't multiple keychains
4532 * - SecPolicy refs are removed, since they can't be externalized
4533 * - SecAccess refs are removed, and potentially translated to entitlements
4535 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4536 * parameter errors due to strict input checks in secd, which only permits
4537 * these constants for calls to SecItemCopyMatching.
4539 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4540 * This permits a query to be reused for non-synchronizable items, or to
4541 * resolve a search based on a persistent item reference for iOS.
4543 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4544 * parameter errors due to strict input checks in secd, which do not permit
4545 * these constants for calls to SecItemUpdate.
4548 SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
4549 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
)
4551 CFMutableDictionaryRef result
= CFDictionaryCreateMutableCopy(NULL
, 0, inOSXDict
);
4552 if (result
== NULL
) {
4557 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4561 /* Match constants are only supported on iOS for SecItemCopyMatching,
4562 * and will generate an error if passed to other SecItem API functions;
4563 * on OS X, they're just ignored if not applicable for the context.
4565 CFDictionaryRemoveValue(result
, kSecMatchPolicy
);
4566 CFDictionaryRemoveValue(result
, kSecMatchItemList
);
4567 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4568 CFDictionaryRemoveValue(result
, kSecMatchIssuers
);
4569 CFDictionaryRemoveValue(result
, kSecMatchEmailAddressIfPresent
);
4570 CFDictionaryRemoveValue(result
, kSecMatchSubjectContains
);
4571 CFDictionaryRemoveValue(result
, kSecMatchCaseInsensitive
);
4572 CFDictionaryRemoveValue(result
, kSecMatchTrustedOnly
);
4573 CFDictionaryRemoveValue(result
, kSecMatchValidOnDate
);
4574 CFDictionaryRemoveValue(result
, kSecMatchLimit
);
4575 CFDictionaryRemoveValue(result
, kSecMatchLimitOne
);
4576 CFDictionaryRemoveValue(result
, kSecMatchLimitAll
);
4580 /* Return constants are not supported on iOS for SecItemUpdate,
4581 * where they will generate an error; on OS X, they're just ignored
4582 * if not applicable for the context.
4584 CFDictionaryRemoveValue(result
, kSecReturnData
);
4585 CFDictionaryRemoveValue(result
, kSecReturnAttributes
);
4586 CFDictionaryRemoveValue(result
, kSecReturnRef
);
4587 CFDictionaryRemoveValue(result
, kSecReturnPersistentRef
);
4591 /* Searching on data is not supported. */
4592 CFDictionaryRemoveValue(result
, kSecValueData
);
4596 /* Searching on access lists is not supported */
4597 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4601 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4602 * cannot specify a keychain search list on iOS
4604 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4606 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4607 * cannot specify a keychain on iOS
4609 CFDictionaryRemoveValue(result
, kSecUseKeychain
);
4611 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4612 * unless kSecAttrAccessGroup has already been specified.
4614 SecAccessRef access
= (SecAccessRef
) CFDictionaryGetValue(result
, kSecAttrAccess
);
4615 CFStringRef accessGroup
= (CFStringRef
) CFDictionaryGetValue(result
, kSecAttrAccessGroup
);
4616 if (access
!= NULL
&& accessGroup
== NULL
) {
4617 /* Translate "InternetAccounts" application group to an access group */
4618 if (errSecSuccess
== SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4619 /* The caller is a valid member of the application group. */
4620 CFStringRef groupName
= CFSTR("appleaccount");
4621 CFTypeRef value
= CFDictionaryGetValue(result
, kSecAttrAuthenticationType
);
4622 if (value
&& CFEqual(value
, kSecAttrAuthenticationTypeHTMLForm
)) {
4623 groupName
= CFSTR("com.apple.cfnetwork");
4625 CFDictionarySetValue(result
, kSecAttrAccessGroup
, groupName
);
4628 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4630 /* If item is specified by direct reference, and this is an iOS search,
4631 * replace it with a persistent reference, if it was recorded inside ItemImpl.
4633 CFTypeRef directRef
= CFDictionaryGetValue(result
, kSecValueRef
);
4634 if (directRef
!= NULL
) {
4635 CFTypeID typeID
= CFGetTypeID(directRef
);
4636 if ((typeID
!= SecKeyGetTypeID() || SecKeyIsCDSAKey((SecKeyRef
)directRef
)) &&
4637 (typeID
!= SecCertificateGetTypeID() || SecCertificateIsItemImplInstance((SecCertificateRef
)directRef
)) &&
4638 (typeID
!= SecIdentityGetTypeID())) {
4639 CFDataRef persistentRef
= _SecItemGetPersistentReference(directRef
);
4640 if (persistentRef
) {
4641 CFDictionarySetValue(result
, kSecValuePersistentRef
, persistentRef
);
4642 CFDictionaryRemoveValue(result
, kSecValueRef
);
4647 /* If item is specified by persistent reference, and this is an iOS search,
4648 * remove the synchronizable attribute as it will be rejected by secd.
4650 CFTypeRef persistentRef
= CFDictionaryGetValue(result
, kSecValuePersistentRef
);
4651 if (persistentRef
) {
4652 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4655 /* Remove kSecAttrModificationDate; this should never be used as criteria
4656 * for a search, or to add/modify an item. (If we are cloning an item
4657 * and want to keep its modification date, we don't call this function.)
4658 * It turns out that some clients are using the full attributes dictionary
4659 * returned by SecItemCopyMatching as a query to find the same item later,
4660 * which won't work once the item is updated.
4662 CFDictionaryRemoveValue(result
, kSecAttrModificationDate
);
4665 /* iOS doesn't add the class attribute, so we must do it here. */
4667 CFDictionarySetValue(result
, kSecClass
, itemClass
);
4669 /* Remove attributes which are not part of the OS X database schema. */
4670 CFDictionaryRemoveValue(result
, kSecAttrAccessible
);
4671 CFDictionaryRemoveValue(result
, kSecAttrAccessControl
);
4672 CFDictionaryRemoveValue(result
, kSecAttrAccessGroup
);
4673 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4674 CFDictionaryRemoveValue(result
, kSecAttrTombstone
);
4677 /* This attribute is consumed by the bridge itself. */
4678 CFDictionaryRemoveValue(result
, kSecAttrNoLegacy
);
4686 SecItemMergeResults(bool can_target_ios
, OSStatus status_ios
, CFTypeRef result_ios
,
4687 bool can_target_osx
, OSStatus status_osx
, CFTypeRef result_osx
,
4688 CFTypeRef
*result
) {
4689 // When querying both keychains and iOS keychain fails because of missing
4690 // entitlements, completely ignore iOS keychain result. This is to keep
4691 // backward compatibility with applications which know nothing about iOS keychain
4692 // and use SecItem API to access OSX keychain which does not need any entitlements.
4693 if (can_target_osx
&& can_target_ios
&& status_ios
== errSecMissingEntitlement
) {
4694 can_target_ios
= false;
4697 if (can_target_osx
&& can_target_ios
) {
4698 // If both keychains were targetted, examine returning statuses and decide what to do.
4699 if (status_ios
!= errSecSuccess
) {
4700 // iOS keychain failed to produce results because of some error, go with results from OSX keychain.
4701 // Since iOS keychain queries will fail without a keychain-access-group or proper entitlements, SecItemCopyMatching
4702 // calls against the OSX keychain API that should return errSecItemNotFound will return nonsense from the iOS keychain.
4703 AssignOrReleaseResult(result_osx
, result
);
4705 } else if (status_osx
!= errSecSuccess
) {
4706 if (status_osx
!= errSecItemNotFound
) {
4707 // OSX failed to produce results with some failure mode (else than not_found), but iOS produced results.
4708 // We have to either return OSX failure result and discard iOS results, or vice versa. For now, we just
4709 // ignore OSX error and return just iOS results.
4710 secitemlog(LOG_NOTICE
, "SecItemMergeResults: osx_result=%d, ignoring it, iOS succeeded fine", status_osx
);
4713 // OSX failed to produce results, but we have success from iOS keychain; go with results from iOS keychain.
4714 AssignOrReleaseResult(result_ios
, result
);
4715 return errSecSuccess
;
4717 // Both searches succeeded, merge results.
4718 if (result
!= NULL
) {
4719 CFTypeID id_osx
= (result_osx
) ? CFGetTypeID(result_osx
) : 0;
4720 CFTypeID id_ios
= (result_ios
) ? CFGetTypeID(result_ios
) : 0;
4721 CFTypeID id_array
= CFArrayGetTypeID();
4722 if ((id_osx
== id_array
) && (id_ios
== id_array
)) {
4723 // Fold the arrays into one.
4724 *result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4725 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_ios
,
4726 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_ios
)));
4727 CFArrayAppendArray((CFMutableArrayRef
)*result
, (CFArrayRef
)result_osx
,
4728 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_osx
)));
4730 // Result type is not an array, so only one match can be returned.
4731 *result
= (id_ios
) ? result_ios
: result_osx
;
4732 CFRetainSafe(*result
);
4735 CFReleaseSafe(result_osx
);
4736 CFReleaseSafe(result_ios
);
4737 return errSecSuccess
;
4739 } else if (can_target_ios
) {
4740 // Only iOS keychain was targetted.
4741 AssignOrReleaseResult(result_ios
, result
);
4743 } else if (can_target_osx
) {
4744 // Only OSX keychain was targetted.
4745 AssignOrReleaseResult(result_osx
, result
);
4748 // Query could not run at all?
4754 ShouldTryUnlockKeybag(CFDictionaryRef query
, OSErr status
)
4756 static __typeof(SASSessionStateForUser
) *soft_SASSessionStateForUser
= NULL
;
4757 static dispatch_once_t onceToken
;
4758 static void *framework
;
4760 if (status
!= errSecInteractionNotAllowed
)
4763 // If the query disabled authUI, respect it.
4764 CFTypeRef authUI
= NULL
;
4766 authUI
= CFDictionaryGetValue(query
, kSecUseAuthenticationUI
);
4767 if (authUI
== NULL
) {
4768 authUI
= CFDictionaryGetValue(query
, kSecUseNoAuthenticationUI
);
4769 authUI
= (authUI
!= NULL
&& CFEqual(authUI
, kCFBooleanTrue
)) ? kSecUseAuthenticationUIFail
: NULL
;
4772 if (authUI
&& !CFEqual(authUI
, kSecUseAuthenticationUIAllow
))
4775 dispatch_once(&onceToken
, ^{
4776 framework
= dlopen("/System/Library/PrivateFrameworks/login.framework/login", RTLD_LAZY
);
4777 if (framework
== NULL
)
4779 soft_SASSessionStateForUser
= (__typeof(soft_SASSessionStateForUser
)) dlsym(framework
, "SASSessionStateForUser");
4782 if (soft_SASSessionStateForUser
== NULL
)
4785 SessionAgentState sessionState
= soft_SASSessionStateForUser(getuid());
4786 if(sessionState
!= kSA_state_desktopshowing
)
4793 SecItemCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
)
4795 os_activity_t activity
= os_activity_create("SecItemCopyMatching", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4796 os_activity_scope(activity
);
4797 os_release(activity
);
4802 secitemshow(query
, "SecItemCopyMatching query:");
4804 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4805 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4806 bool can_target_ios
, can_target_osx
;
4807 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
4808 if (status
!= errSecSuccess
) {
4812 if (can_target_ios
) {
4813 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4814 CFDictionaryGetValue(query
, kSecClass
), true, false, false, false, true, true);
4816 status_ios
= errSecParam
;
4819 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4820 if(ShouldTryUnlockKeybag(query
, status_ios
)) {
4821 // The keybag is locked. Attempt to unlock it...
4822 secitemlog(LOG_WARNING
, "SecItemCopyMatching triggering SecurityAgent");
4823 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4824 CFReleaseNull(result_ios
);
4825 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4828 CFRelease(attrs_ios
);
4830 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_ios result: %d", status_ios
);
4833 if (can_target_osx
) {
4834 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4835 CFDictionaryGetValue(query
, kSecClass
), false, false, true, false, true, true);
4837 status_osx
= errSecParam
;
4840 status_osx
= SecItemCopyMatching_osx(attrs_osx
, &result_osx
);
4841 CFRelease(attrs_osx
);
4843 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_osx result: %d", status_osx
);
4846 status
= SecItemMergeResults(can_target_ios
, status_ios
, result_ios
,
4847 can_target_osx
, status_osx
, result_osx
, result
);
4848 secitemlog(LOG_NOTICE
, "SecItemCopyMatching result: %d", status
);
4853 SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
)
4855 os_activity_t activity
= os_activity_create("SecItemAdd", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4856 os_activity_scope(activity
);
4857 os_release(activity
);
4865 secitemshow(attributes
, "SecItemAdd attrs:");
4867 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4868 bool can_target_ios
, can_target_osx
;
4869 OSStatus status
= SecItemCategorizeQuery(attributes
, can_target_ios
, can_target_osx
);
4870 if (status
!= errSecSuccess
) {
4874 // SecItemAdd cannot be really done on both keychains. In order to keep backward compatibility
4875 // with existing applications, we prefer to add items into legacy keychain and fallback
4876 // into iOS (modern) keychain only when the query is not suitable for legacy keychain.
4877 if (!can_target_osx
) {
4878 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(attributes
,
4879 NULL
, true, true, false, false, false, false);
4881 status
= errSecParam
;
4883 status
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4884 if(ShouldTryUnlockKeybag(attributes
, status
)) {
4885 // The keybag is locked. Attempt to unlock it...
4886 secitemlog(LOG_WARNING
, "SecItemAdd triggering SecurityAgent");
4887 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(3)) {
4888 CFReleaseNull(result_ios
);
4889 status
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4892 CFRelease(attrs_ios
);
4894 secitemlog(LOG_NOTICE
, "SecItemAdd_ios result: %d", status
);
4895 AssignOrReleaseResult(result_ios
, result
);
4898 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(attributes
,
4899 NULL
, false, false, true, false, false, false);
4901 status
= errSecParam
;
4903 status
= SecItemAdd_osx(attrs_osx
, &result_osx
);
4904 CFRelease(attrs_osx
);
4906 secitemlog(LOG_NOTICE
, "SecItemAdd_osx result: %d", status
);
4907 AssignOrReleaseResult(result_osx
, result
);
4913 SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
)
4915 os_activity_t activity
= os_activity_create("SecItemUpdate", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4916 os_activity_scope(activity
);
4917 os_release(activity
);
4919 if (!query
|| !attributesToUpdate
) {
4922 secitemshow(query
, "SecItemUpdate query:");
4923 secitemshow(attributesToUpdate
, "SecItemUpdate attrs:");
4925 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4926 bool can_target_ios
, can_target_osx
;
4927 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
4928 if (status
!= errSecSuccess
) {
4932 if (can_target_ios
) {
4933 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4934 CFDictionaryGetValue(query
, kSecClass
), true, true, false, true, true, true);
4936 status_ios
= errSecParam
;
4939 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate
)) {
4940 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
4941 if(ShouldTryUnlockKeybag(query
, status_ios
)) {
4942 // The keybag is locked. Attempt to unlock it...
4943 secitemlog(LOG_WARNING
, "SecItemUpdate triggering SecurityAgent");
4944 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4945 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
4949 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
4950 if(ShouldTryUnlockKeybag(query
, status_ios
)) {
4951 // The keybag is locked. Attempt to unlock it...
4952 secitemlog(LOG_WARNING
, "SecItemUpdate triggering SecurityAgent");
4953 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
4954 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
4958 CFRelease(attrs_ios
);
4960 secitemlog(LOG_NOTICE
, "SecItemUpdate_ios result: %d", status_ios
);
4963 if (can_target_osx
) {
4964 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4965 CFDictionaryGetValue(query
, kSecClass
), false, false, true, true, true, true);
4967 status_osx
= errSecParam
;
4970 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate
))
4971 status_osx
= SecItemChangeSynchronizability(attrs_osx
, attributesToUpdate
, true);
4973 status_osx
= SecItemUpdate_osx(attrs_osx
, attributesToUpdate
);
4975 CFRelease(attrs_osx
);
4977 secitemlog(LOG_NOTICE
, "SecItemUpdate_osx result: %d", status_osx
);
4980 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
4981 can_target_osx
, status_osx
, NULL
, NULL
);
4982 secitemlog(LOG_NOTICE
, "SecItemUpdate result: %d", status
);
4987 SecItemDelete(CFDictionaryRef query
)
4989 os_activity_t activity
= os_activity_create("SecItemDelete", OS_ACTIVITY_CURRENT
, OS_ACTIVITY_FLAG_DEFAULT
);
4990 os_activity_scope(activity
);
4991 os_release(activity
);
4996 secitemshow(query
, "SecItemDelete query:");
4998 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4999 bool can_target_ios
, can_target_osx
;
5000 OSStatus status
= SecItemCategorizeQuery(query
, can_target_ios
, can_target_osx
);
5001 if (status
!= errSecSuccess
) {
5005 if (can_target_ios
) {
5006 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
5007 NULL
, true, true, false, true, true, true);
5009 status_ios
= errSecParam
;
5011 status_ios
= SecItemDelete_ios(attrs_ios
);
5012 CFRelease(attrs_ios
);
5014 secitemlog(LOG_NOTICE
, "SecItemDelete_ios result: %d", status_ios
);
5017 if (can_target_osx
) {
5018 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
5019 NULL
, false, false, true, true, true, true);
5021 status_osx
= errSecParam
;
5023 status_osx
= SecItemDelete_osx(attrs_osx
);
5024 CFRelease(attrs_osx
);
5026 secitemlog(LOG_NOTICE
, "SecItemDelete_osx result: %d", status_osx
);
5029 status
= SecItemMergeResults(can_target_ios
, status_ios
, NULL
,
5030 can_target_osx
, status_osx
, NULL
, NULL
);
5031 secitemlog(LOG_NOTICE
, "SecItemCopyDelete result: %d", status
);
5036 SecItemUpdateTokenItems(CFTypeRef tokenID
, CFArrayRef tokenItemsAttributes
)
5038 OSStatus status
= SecItemUpdateTokenItems_ios(tokenID
, tokenItemsAttributes
);
5039 if(ShouldTryUnlockKeybag(NULL
, status
)) {
5040 // The keybag is locked. Attempt to unlock it...
5041 if(errSecSuccess
== SecKeychainVerifyKeyStorePassphrase(1)) {
5042 secitemlog(LOG_WARNING
, "SecItemUpdateTokenItems triggering SecurityAgent");
5043 status
= SecItemUpdateTokenItems_ios(tokenID
, tokenItemsAttributes
);
5046 secitemlog(LOG_NOTICE
, "SecItemUpdateTokenItems_ios result: %d", status
);
5051 SecItemCopyMatching_osx(
5052 CFDictionaryRef query
,
5055 if (!query
|| !result
)
5060 CFAllocatorRef allocator
= CFGetAllocator(query
);
5061 CFIndex matchCount
= 0;
5062 CFMutableArrayRef itemArray
= NULL
;
5063 SecKeychainItemRef item
= NULL
;
5064 SecIdentityRef identity
= NULL
;
5065 OSStatus tmpStatus
, status
= errSecSuccess
;
5067 // validate input query parameters and create the search reference
5068 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(query
, &status
);
5069 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
5071 // find the next match until we hit maxMatches, or no more matches found
5072 while ( !(!itemParams
->returnAllMatches
&& matchCount
>= itemParams
->maxMatches
) &&
5073 SecItemSearchCopyNext(itemParams
, (CFTypeRef
*)&item
) == errSecSuccess
) {
5075 if (FilterCandidateItem((CFTypeRef
*)&item
, itemParams
, &identity
))
5076 continue; // move on to next item
5078 ++matchCount
; // we have a match
5080 tmpStatus
= AddItemResults(item
, identity
, itemParams
, allocator
, &itemArray
, result
);
5081 if (tmpStatus
&& (status
== errSecSuccess
))
5089 CFRelease(identity
);
5094 if (status
== errSecSuccess
)
5095 status
= (matchCount
> 0) ? errSecSuccess
: errSecItemNotFound
;
5098 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5102 _FreeSecItemParams(itemParams
);
5108 SecItemCopyDisplayNames(
5110 CFArrayRef
*displayNames
)
5114 Required(displayNames
);
5116 return errSecUnimplemented
;
5122 CFDictionaryRef attributes
,
5130 CFAllocatorRef allocator
= CFGetAllocator(attributes
);
5131 CFMutableArrayRef itemArray
= NULL
;
5132 SecKeychainItemRef item
= NULL
;
5133 OSStatus tmpStatus
, status
= errSecSuccess
;
5135 // validate input attribute parameters
5136 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(attributes
, &status
);
5137 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
5139 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
5140 // since the private key should already be in a keychain by definition. We could support
5141 // this as a copy operation for the private key if a different keychain is specified,
5142 // but in any case it should try to add the certificate. See <rdar://8317887>.
5143 require_action(!itemParams
->returnIdentity
, error_exit
, status
= errSecItemInvalidValue
);
5145 if (itemParams
->useItems
== NULL
) {
5147 require_action(itemParams
->itemData
== NULL
|| CFGetTypeID(itemParams
->itemData
) == CFDataGetTypeID(),
5148 error_exit
, status
= errSecItemInvalidValue
);
5150 // create a single keychain item specified by the input attributes
5151 status
= SecKeychainItemCreateFromContent(itemParams
->itemClass
,
5152 itemParams
->attrList
,
5153 (itemParams
->itemData
) ? (UInt32
)CFDataGetLength(itemParams
->itemData
) : 0,
5154 (itemParams
->itemData
) ? CFDataGetBytePtrVoid(itemParams
->itemData
) : NULL
,
5155 itemParams
->keychain
,
5158 require_noerr(status
, error_exit
);
5160 // return results (if requested)
5162 itemParams
->maxMatches
= 1; // in case kSecMatchLimit was set to > 1
5163 tmpStatus
= AddItemResults(item
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5164 if (tmpStatus
&& (status
== errSecSuccess
))
5170 // add multiple items which are specified in the itemParams->useItems array.
5171 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
5172 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
5173 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
5175 OSStatus aggregateStatus
= errSecSuccess
;
5176 CFIndex ix
, count
= CFArrayGetCount(itemParams
->useItems
);
5177 itemParams
->maxMatches
= (count
> 1) ? (int)count
: 2; // force results to always be returned as an array
5178 for (ix
=0; ix
< count
; ix
++) {
5179 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->useItems
, ix
);
5181 if (SecCertificateGetTypeID() == CFGetTypeID(anItem
)) {
5182 // SecCertificateRef item
5183 tmpStatus
= SecCertificateAddToKeychain((SecCertificateRef
)anItem
, itemParams
->keychain
);
5184 if (!tmpStatus
&& result
) {
5185 tmpStatus
= AddItemResults((SecKeychainItemRef
)anItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5187 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5189 else if (SecKeyGetTypeID() == CFGetTypeID(anItem
)) {
5191 SecKeychainRef itemKeychain
= NULL
;
5192 tmpStatus
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)anItem
, &itemKeychain
);
5193 if (tmpStatus
== errSecSuccess
) {
5194 // key was in a keychain, so we can attempt to copy it
5195 SecKeychainItemRef itemCopy
= NULL
;
5196 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5197 if (!tmpStatus
&& result
) {
5198 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5201 CFRelease(itemCopy
);
5205 // key was not in any keychain, so must be imported
5206 SecKeychainItemRef keyItem
= NULL
;
5207 tmpStatus
= _ImportKey((SecKeyRef
)anItem
, itemParams
->keychain
, itemParams
->access
, itemParams
->attrList
, &keyItem
);
5208 if (!tmpStatus
&& result
) {
5209 tmpStatus
= AddItemResults(keyItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5216 CFRelease(itemKeychain
);
5218 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5220 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem
)) {
5221 // SecKeychainItemRef item
5222 SecKeychainItemRef itemCopy
= NULL
;
5223 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5224 if (!tmpStatus
&& result
) {
5225 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5228 CFRelease(itemCopy
);
5230 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5232 else if (CFDataGetTypeID() == CFGetTypeID(anItem
)) {
5233 // CFDataRef item (persistent reference)
5234 SecKeychainItemRef realItem
= NULL
;
5235 tmpStatus
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
);
5236 if (tmpStatus
== errSecSuccess
) {
5237 // persistent reference resolved to a keychain item, so we can attempt to copy it
5238 SecKeychainItemRef itemCopy
= NULL
;
5239 tmpStatus
= SecKeychainItemCreateCopy(realItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
5240 if (!tmpStatus
&& result
) {
5241 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
5244 CFRelease(itemCopy
);
5248 CFRelease(realItem
);
5250 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
5253 } // end of itemList array loop
5254 status
= aggregateStatus
;
5255 } // end processing multiple items
5258 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
5262 _FreeSecItemParams(itemParams
);
5269 CFDictionaryRef query
,
5270 CFDictionaryRef attributesToUpdate
)
5272 if (!query
|| !attributesToUpdate
)
5275 // run the provided query to get a list of items to update
5276 CFTypeRef results
= NULL
;
5277 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5278 if (status
!= errSecSuccess
)
5279 return status
; // nothing was matched, or the query was bad
5281 CFArrayRef items
= NULL
;
5282 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5283 items
= (CFArrayRef
) results
;
5286 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5290 OSStatus result
= errSecSuccess
;
5291 CFIndex ix
, count
= CFArrayGetCount(items
);
5292 for (ix
=0; ix
< count
; ix
++) {
5293 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5295 status
= _UpdateKeychainItem(anItem
, attributesToUpdate
);
5296 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);
5308 CFDictionaryRef query
)
5313 // run the provided query to get a list of items to delete
5314 CFTypeRef results
= NULL
;
5315 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
5316 if (status
!= errSecSuccess
)
5317 return status
; // nothing was matched, or the query was bad
5319 CFArrayRef items
= NULL
;
5320 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
5321 items
= (CFArrayRef
) results
;
5324 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
5328 OSStatus result
= errSecSuccess
;
5329 CFIndex ix
, count
= CFArrayGetCount(items
);
5330 for (ix
=0; ix
< count
; ix
++) {
5331 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
5333 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
)) {
5334 status
= _DeleteIdentity((SecIdentityRef
)anItem
);
5337 status
= _DeleteKeychainItem(anItem
);
5339 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);