2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecBridge.h"
25 #include "SecInternal.h"
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <security_utilities/cfutilities.h>
28 #include <Security/SecBase.h>
29 #include <Security/SecKeychainItem.h>
30 #include <Security/SecCertificate.h>
31 #include <sys/param.h>
32 #include "cssmdatetime.h"
34 #include "SecItemPriv.h"
35 #include "SecIdentitySearchPriv.h"
36 #include "SecKeychainPriv.h"
37 #include "SecCertificatePriv.h"
38 #include "SecCertificatePrivP.h"
39 #include "TrustAdditions.h"
41 #include <AssertMacros.h>
44 #include <Security/SecTrustedApplication.h>
45 #include <Security/SecTrustedApplicationPriv.h>
46 #include <Security/SecCode.h>
47 #include <Security/SecCodePriv.h>
48 #include <Security/SecRequirement.h>
50 const uint8_t kUUIDStringLength
= 36;
52 OSStatus
SecItemAdd_osx(CFDictionaryRef attributes
, CFTypeRef
*result
);
53 OSStatus
SecItemCopyMatching_osx(CFDictionaryRef query
, CFTypeRef
*result
);
54 OSStatus
SecItemUpdate_osx(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
55 OSStatus
SecItemDelete_osx(CFDictionaryRef query
);
58 OSStatus
SecItemAdd_ios(CFDictionaryRef attributes
, CFTypeRef
*result
);
59 OSStatus
SecItemCopyMatching_ios(CFDictionaryRef query
, CFTypeRef
*result
);
60 OSStatus
SecItemUpdate_ios(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
);
61 OSStatus
SecItemDelete_ios(CFDictionaryRef query
);
63 CFTypeRef
SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
);
64 CFTypeRef
SecItemCopyMergedResults(CFDictionaryRef query
, CFTypeRef result_osx
, CFTypeRef result_ios
);
65 OSStatus
SecItemValidateAppleApplicationGroupAccess(CFStringRef group
);
66 CFDictionaryRef
SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
67 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
);
70 static Boolean
SecItemSynchronizable(CFDictionaryRef query
);
72 static void secitemlog(int priority
, const char *format
, ...)
77 if (priority
< LOG_NOTICE
) // log warnings and errors
81 va_start(list
, format
);
82 vsyslog(priority
, format
, list
);
87 static void secitemshow(CFTypeRef obj
, const char *context
)
90 CFStringRef desc
= CFCopyDescription(obj
);
93 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc
), kCFStringEncodingUTF8
) + 1;
94 char* buffer
= (char*) malloc(length
);
96 Boolean converted
= CFStringGetCString(desc
, buffer
, length
, kCFStringEncodingUTF8
);
98 const char *prefix
= (context
) ? context
: "";
99 const char *separator
= (context
) ? " " : "";
100 secitemlog(LOG_NOTICE
, "%s%s%s", prefix
, separator
, buffer
);
109 #define CFDataGetBytePtrVoid CFDataGetBytePtr
111 #pragma mark SecItem private utility functions
113 /******************************************************************************/
115 struct ProtocolAttributeInfo
{
116 const CFStringRef
*protocolValue
;
117 SecProtocolType protocolType
;
120 static ProtocolAttributeInfo gProtocolTypes
[] = {
121 { &kSecAttrProtocolFTP
, kSecProtocolTypeFTP
},
122 { &kSecAttrProtocolFTPAccount
, kSecProtocolTypeFTPAccount
},
123 { &kSecAttrProtocolHTTP
, kSecProtocolTypeHTTP
},
124 { &kSecAttrProtocolIRC
, kSecProtocolTypeIRC
},
125 { &kSecAttrProtocolNNTP
, kSecProtocolTypeNNTP
},
126 { &kSecAttrProtocolPOP3
, kSecProtocolTypePOP3
},
127 { &kSecAttrProtocolSMTP
, kSecProtocolTypeSMTP
},
128 { &kSecAttrProtocolSOCKS
, kSecProtocolTypeSOCKS
},
129 { &kSecAttrProtocolIMAP
, kSecProtocolTypeIMAP
},
130 { &kSecAttrProtocolLDAP
, kSecProtocolTypeLDAP
},
131 { &kSecAttrProtocolAppleTalk
, kSecProtocolTypeAppleTalk
},
132 { &kSecAttrProtocolAFP
, kSecProtocolTypeAFP
},
133 { &kSecAttrProtocolTelnet
, kSecProtocolTypeTelnet
},
134 { &kSecAttrProtocolSSH
, kSecProtocolTypeSSH
},
135 { &kSecAttrProtocolFTPS
, kSecProtocolTypeFTPS
},
136 { &kSecAttrProtocolHTTPS
, kSecProtocolTypeHTTPS
},
137 { &kSecAttrProtocolHTTPProxy
, kSecProtocolTypeHTTPProxy
},
138 { &kSecAttrProtocolHTTPSProxy
, kSecProtocolTypeHTTPSProxy
},
139 { &kSecAttrProtocolFTPProxy
, kSecProtocolTypeFTPProxy
},
140 { &kSecAttrProtocolSMB
, kSecProtocolTypeSMB
},
141 { &kSecAttrProtocolRTSP
, kSecProtocolTypeRTSP
},
142 { &kSecAttrProtocolRTSPProxy
, kSecProtocolTypeRTSPProxy
},
143 { &kSecAttrProtocolDAAP
, kSecProtocolTypeDAAP
},
144 { &kSecAttrProtocolEPPC
, kSecProtocolTypeEPPC
},
145 { &kSecAttrProtocolIPP
, kSecProtocolTypeIPP
},
146 { &kSecAttrProtocolNNTPS
, kSecProtocolTypeNNTPS
},
147 { &kSecAttrProtocolLDAPS
, kSecProtocolTypeLDAPS
},
148 { &kSecAttrProtocolTelnetS
, kSecProtocolTypeTelnetS
},
149 { &kSecAttrProtocolIMAPS
, kSecProtocolTypeIMAPS
},
150 { &kSecAttrProtocolIRCS
, kSecProtocolTypeIRCS
},
151 { &kSecAttrProtocolPOP3S
, kSecProtocolTypePOP3S
}
154 static const int kNumberOfProtocolTypes
= sizeof(gProtocolTypes
) / sizeof(ProtocolAttributeInfo
);
157 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
159 static SecProtocolType
160 _SecProtocolTypeForSecAttrProtocol(
163 SecProtocolType result
= kSecProtocolTypeAny
;
165 if (protocol
!= NULL
) {
167 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
168 if (CFEqual(protocol
, *(gProtocolTypes
[count
].protocolValue
))) {
169 result
= gProtocolTypes
[count
].protocolType
;
179 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
182 _SecAttrProtocolForSecProtocolType(
183 SecProtocolType protocolType
)
185 CFTypeRef result
= NULL
;
187 for (count
=0; count
<kNumberOfProtocolTypes
; count
++) {
188 if (gProtocolTypes
[count
].protocolType
== protocolType
) {
189 result
= *(gProtocolTypes
[count
].protocolValue
);
198 /******************************************************************************/
200 struct AuthenticationAttributeInfo
{
201 const CFStringRef
*authValue
;
202 SecAuthenticationType authType
;
205 static AuthenticationAttributeInfo gAuthTypes
[] = {
206 { &kSecAttrAuthenticationTypeNTLM
, kSecAuthenticationTypeNTLM
},
207 { &kSecAttrAuthenticationTypeMSN
, kSecAuthenticationTypeMSN
},
208 { &kSecAttrAuthenticationTypeDPA
, kSecAuthenticationTypeDPA
},
209 { &kSecAttrAuthenticationTypeRPA
, kSecAuthenticationTypeRPA
},
210 { &kSecAttrAuthenticationTypeHTTPBasic
, kSecAuthenticationTypeHTTPBasic
},
211 { &kSecAttrAuthenticationTypeHTTPDigest
, kSecAuthenticationTypeHTTPDigest
},
212 { &kSecAttrAuthenticationTypeHTMLForm
, kSecAuthenticationTypeHTMLForm
},
213 { &kSecAttrAuthenticationTypeDefault
, kSecAuthenticationTypeDefault
}
216 static const int kNumberOfAuthenticationTypes
= sizeof(gAuthTypes
) / sizeof(AuthenticationAttributeInfo
);
219 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
220 * SecAttrAuthenticationType to a SecAuthenticationType.
222 static SecAuthenticationType
223 _SecAuthenticationTypeForSecAttrAuthenticationType(
224 CFTypeRef authenticationType
)
226 SecAuthenticationType result
= kSecAuthenticationTypeAny
;
228 if (authenticationType
!= NULL
) {
230 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
231 if (CFEqual(authenticationType
, *(gAuthTypes
[count
].authValue
))) {
232 result
= gAuthTypes
[count
].authType
;
242 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
243 * to a SecAttrAuthenticationType.
246 _SecAttrAuthenticationTypeForSecAuthenticationType(
247 SecAuthenticationType authenticationType
)
249 CFTypeRef result
= NULL
;
251 for (count
=0; count
<kNumberOfAuthenticationTypes
; count
++) {
252 if (gAuthTypes
[count
].authType
== authenticationType
) {
253 result
= *(gAuthTypes
[count
].authValue
);
262 /******************************************************************************/
264 struct KeyAlgorithmInfo
{
265 const CFStringRef
*keyType
;
269 static KeyAlgorithmInfo gKeyTypes
[] = {
270 { &kSecAttrKeyTypeRSA
, CSSM_ALGID_RSA
},
271 { &kSecAttrKeyTypeDSA
, CSSM_ALGID_DSA
},
272 { &kSecAttrKeyTypeAES
, CSSM_ALGID_AES
},
273 { &kSecAttrKeyTypeDES
, CSSM_ALGID_DES
},
274 { &kSecAttrKeyType3DES
, CSSM_ALGID_3DES
},
275 { &kSecAttrKeyTypeRC4
, CSSM_ALGID_RC4
},
276 { &kSecAttrKeyTypeRC2
, CSSM_ALGID_RC2
},
277 { &kSecAttrKeyTypeCAST
, CSSM_ALGID_CAST
},
278 { &kSecAttrKeyTypeECDSA
, CSSM_ALGID_ECDSA
},
279 { &kSecAttrKeyTypeEC
, CSSM_ALGID_ECDSA
}
282 static const int kNumberOfKeyTypes
= sizeof(gKeyTypes
) / sizeof (KeyAlgorithmInfo
);
285 static UInt32
_SecAlgorithmTypeFromSecAttrKeyType(
286 CFTypeRef keyTypeRef
)
288 UInt32 keyAlgValue
= 0;
289 if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef
))
293 for (ix
=0; ix
<kNumberOfKeyTypes
; ix
++) {
294 if (CFEqual(keyTypeRef
, *(gKeyTypes
[ix
].keyType
))) {
295 keyAlgValue
= gKeyTypes
[ix
].keyValue
;
300 //%%%TODO try to convert the input string to a number here
306 enum ItemRepresentation
308 kStringRepresentation
,
310 kNumberRepresentation
,
311 kBooleanRepresentation
,
316 struct InternalAttributeListInfo
319 const CFStringRef
*newItemType
;
320 ItemRepresentation itemRepresentation
;
324 static InternalAttributeListInfo gGenericPasswordAttributes
[] =
326 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
327 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
328 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
329 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
330 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
331 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
332 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
333 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
334 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
335 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
336 { kSecServiceItemAttr
, &kSecAttrService
, kStringRepresentation
},
337 { kSecGenericItemAttr
, &kSecAttrGeneric
, kDataRepresentation
}
340 static const int kNumberOfGenericPasswordAttributes
= sizeof(gGenericPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
343 static InternalAttributeListInfo gInternetPasswordAttributes
[] =
345 { kSecCreationDateItemAttr
, &kSecAttrCreationDate
, kDateRepresentation
},
346 { kSecModDateItemAttr
, &kSecAttrModificationDate
, kDateRepresentation
},
347 { kSecDescriptionItemAttr
, &kSecAttrDescription
, kStringRepresentation
},
348 { kSecCommentItemAttr
, &kSecAttrComment
, kStringRepresentation
},
349 { kSecCreatorItemAttr
, &kSecAttrCreator
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
350 { kSecTypeItemAttr
, &kSecAttrType
, kNumberRepresentation
}, // UInt32, a.k.a. FourCharCode
351 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
352 { kSecInvisibleItemAttr
, &kSecAttrIsInvisible
, kBooleanRepresentation
},
353 { kSecNegativeItemAttr
, &kSecAttrIsNegative
, kBooleanRepresentation
},
354 { kSecAccountItemAttr
, &kSecAttrAccount
, kStringRepresentation
},
355 { kSecSecurityDomainItemAttr
, &kSecAttrSecurityDomain
, kStringRepresentation
},
356 { kSecServerItemAttr
, &kSecAttrServer
, kStringRepresentation
},
357 { kSecAuthenticationTypeItemAttr
, &kSecAttrAuthenticationType
, kStringRepresentation
}, // maps from UInt32 value to string constant
358 { kSecPortItemAttr
, &kSecAttrPort
, kNumberRepresentation
},
359 { kSecPathItemAttr
, &kSecAttrPath
, kStringRepresentation
}
362 static const int kNumberOfInternetPasswordAttributes
= sizeof(gInternetPasswordAttributes
) / sizeof (InternalAttributeListInfo
);
365 static InternalAttributeListInfo gCertificateAttributes
[] =
367 { kSecLabelItemAttr
, &kSecAttrLabel
, kStringRepresentation
},
368 { kSecSubjectItemAttr
, &kSecAttrSubject
, kDataRepresentation
},
369 { kSecIssuerItemAttr
, &kSecAttrIssuer
, kDataRepresentation
},
370 { kSecSerialNumberItemAttr
, &kSecAttrSerialNumber
, kDataRepresentation
},
371 { kSecPublicKeyHashItemAttr
, &kSecAttrPublicKeyHash
, kDataRepresentation
},
372 { kSecSubjectKeyIdentifierItemAttr
, &kSecAttrSubjectKeyID
, kDataRepresentation
},
373 { kSecCertTypeItemAttr
, &kSecAttrCertificateType
, kDataRepresentation
},
374 { kSecCertEncodingItemAttr
, &kSecAttrCertificateEncoding
, kDataRepresentation
}
377 static const int kNumberOfCertificateAttributes
= sizeof(gCertificateAttributes
) / sizeof(InternalAttributeListInfo
);
380 static InternalAttributeListInfo gKeyAttributes
[] =
382 { kSecKeyKeyClass
, &kSecAttrKeyClass
, kStringRepresentation
}, // key class maps from UInt32 value to string constant
383 { kSecKeyPrintName
, &kSecAttrLabel
, kStringRepresentation
}, // note that "print name" maps to the user-visible label
384 // { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
385 { kSecKeyPermanent
, &kSecAttrIsPermanent
, kBooleanRepresentation
},
386 // { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
387 // { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
388 { 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
389 { kSecKeyApplicationTag
, &kSecAttrApplicationTag
, kDataRepresentation
},
390 // { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
391 { kSecKeyKeyType
, &kSecAttrKeyType
, kStringRepresentation
}, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
392 { kSecKeyKeySizeInBits
, &kSecAttrKeySizeInBits
, kNumberRepresentation
},
393 { kSecKeyEffectiveKeySize
, &kSecAttrEffectiveKeySize
, kNumberRepresentation
},
394 // { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
395 // { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
396 // { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
397 // { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
398 // { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
399 // { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
400 { kSecKeyEncrypt
, &kSecAttrCanEncrypt
, kBooleanRepresentation
},
401 { kSecKeyDecrypt
, &kSecAttrCanDecrypt
, kBooleanRepresentation
},
402 { kSecKeyDerive
, &kSecAttrCanDerive
, kBooleanRepresentation
},
403 { kSecKeySign
, &kSecAttrCanSign
, kBooleanRepresentation
},
404 { kSecKeyVerify
, &kSecAttrCanVerify
, kBooleanRepresentation
},
405 // { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
406 // { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
407 { kSecKeyWrap
, &kSecAttrCanWrap
, kBooleanRepresentation
},
408 { kSecKeyUnwrap
, &kSecAttrCanUnwrap
, kBooleanRepresentation
}
411 static const int kNumberOfKeyAttributes
= sizeof(gKeyAttributes
) / sizeof(InternalAttributeListInfo
);
414 static void* CloneDataByType(ItemRepresentation type
, CFTypeRef value
, UInt32
& length
)
418 case kStringRepresentation
:
420 if (CFStringGetTypeID() != CFGetTypeID(value
)) {
424 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
425 char* buffer
= (char*) malloc(maxLength
);
426 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
428 length
= (UInt32
)strlen(buffer
);
438 case kDataRepresentation
:
440 if (CFStringGetTypeID() == CFGetTypeID(value
)) {
441 // We may have a string here, since the key label may be a GUID for the symmetric keys
442 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef
) value
), kCFStringEncodingUTF8
) + 1;
443 char* buffer
= (char*) malloc(maxLength
);
444 Boolean converted
= CFStringGetCString((CFStringRef
) value
, buffer
, maxLength
, kCFStringEncodingUTF8
);
446 length
= (UInt32
)strlen(buffer
);
456 if (CFDataGetTypeID() != CFGetTypeID(value
)) {
460 length
= (UInt32
)CFDataGetLength((CFDataRef
) value
);
461 uint8_t* buffer
= (uint8_t*) malloc(length
);
462 CFDataGetBytes((CFDataRef
) value
, CFRangeMake(0, length
), buffer
);
466 case kNumberRepresentation
:
468 if (CFNumberGetTypeID() != CFGetTypeID(value
)) {
472 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
473 Boolean converted
= CFNumberGetValue((CFNumberRef
) value
, kCFNumberSInt32Type
, buffer
);
475 length
= sizeof(uint32_t);
485 case kBooleanRepresentation
:
487 if (CFBooleanGetTypeID() != CFGetTypeID(value
)) {
491 uint32_t* buffer
= (uint32_t*) malloc(sizeof(uint32_t));
492 *buffer
= (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
493 length
= sizeof(uint32_t);
497 case kDateRepresentation
:
499 if (CFDateGetTypeID() != CFGetTypeID(value
)) {
503 char* buffer
= (char*) calloc(1, 32); // max length of a CSSM date string
504 CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef
) value
, buffer
);
505 length
= (UInt32
)strlen(buffer
);
519 _ConvertNewFormatToOldFormat(
520 CFAllocatorRef allocator
,
521 const InternalAttributeListInfo
* info
,
523 CFDictionaryRef dictionaryRef
,
524 SecKeychainAttributeList
* &attrList
527 // get the keychain attributes array from the data item
528 // here's the problem. On the one hand, we have a dictionary that is purported to contain
529 // attributes for our type. On the other hand, the dictionary may contain items we don't support,
530 // and we therefore don't know how many attributes we will have unless we count them first
533 attrList
= (SecKeychainAttributeList
*) calloc(1, sizeof(SecKeychainAttributeList
));
535 // make storage to extract the dictionary items
536 CFIndex itemsInDictionary
= CFDictionaryGetCount(dictionaryRef
);
537 CFTypeRef keys
[itemsInDictionary
];
538 CFTypeRef values
[itemsInDictionary
];
540 CFTypeRef
*keysPtr
= keys
;
541 CFTypeRef
*valuesPtr
= values
;
543 CFDictionaryGetKeysAndValues(dictionaryRef
, keys
, values
);
545 // count the number of items we are interested in
549 // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
550 // we don't pay the price for this twice
551 SecKeychainAttrType tags
[itemsInDictionary
];
552 ItemRepresentation types
[itemsInDictionary
];
554 for (i
= 0; i
< itemsInDictionary
; ++i
)
556 CFTypeRef key
= keysPtr
[i
];
559 for (j
= 0; j
< infoNumItems
; ++j
)
561 if (CFEqual(*(info
[j
].newItemType
), key
))
563 tags
[i
] = info
[j
].oldItemType
;
564 types
[i
] = info
[j
].itemRepresentation
;
570 if (j
>= infoNumItems
)
572 // if we got here, we aren't interested in this item.
577 // now we can make the result array
578 attrList
->count
= (UInt32
)count
;
579 attrList
->attr
= (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * count
);
581 // fill out the array
582 int resultPointer
= 0;
583 for (i
= 0; i
< itemsInDictionary
; ++i
)
585 if (values
[i
] != NULL
)
587 attrList
->attr
[resultPointer
].tag
= tags
[i
];
589 // we have to clone the data pointer. The caller will need to make sure to throw these away
590 // with _FreeAttrList when it is done...
591 attrList
->attr
[resultPointer
].data
= CloneDataByType(types
[i
], valuesPtr
[i
], attrList
->attr
[resultPointer
].length
);
596 return errSecSuccess
;
602 _ConvertOldFormatToNewFormat(
603 CFAllocatorRef allocator
,
604 const InternalAttributeListInfo
* info
,
606 SecKeychainItemRef itemRef
,
607 CFMutableDictionaryRef
& dictionaryRef
)
609 SecKeychainAttributeList list
;
610 list
.count
= infoNumItems
;
611 list
.attr
= (SecKeychainAttribute
*) calloc(infoNumItems
, sizeof(SecKeychainAttribute
));
613 // fill out the array. We only need to fill in the tags, since calloc zeros what it returns
615 for (i
= 0; i
< infoNumItems
; ++i
)
617 list
.attr
[i
].tag
= info
[i
].oldItemType
;
620 OSStatus result
= SecKeychainItemCopyContent(itemRef
, NULL
, &list
, NULL
, NULL
);
621 if (result
!= errSecSuccess
)
623 dictionaryRef
= NULL
;
628 // create the dictionary
629 dictionaryRef
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
632 for (i
= 0; i
< infoNumItems
; ++i
)
634 if (list
.attr
[i
].data
== NULL
)
637 switch (info
[i
].itemRepresentation
)
639 case kStringRepresentation
:
641 CFStringRef stringRef
;
642 if (info
[i
].oldItemType
== kSecKeyKeyClass
) {
643 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
644 uint32_t keyRecordValue
= *((uint32_t*)list
.attr
[i
].data
);
645 bool retainString
= true;
646 switch (keyRecordValue
) {
647 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
648 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
650 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
651 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
653 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
654 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
657 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyRecordValue
);
661 if (retainString
) CFRetain(stringRef
);
662 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
663 CFRelease(stringRef
);
666 else if (info
[i
].oldItemType
== kSecKeyKeyType
) {
667 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
668 uint32_t keyAlgValue
= *((uint32_t*)list
.attr
[i
].data
);
669 bool retainString
= true;
670 switch (keyAlgValue
) {
671 case CSSM_ALGID_RSA
:
672 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
674 case CSSM_ALGID_DSA
:
675 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
677 case CSSM_ALGID_AES
:
678 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
680 case CSSM_ALGID_DES
:
681 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
683 case CSSM_ALGID_3DES
:
684 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
686 case CSSM_ALGID_RC4
:
687 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
689 case CSSM_ALGID_RC2
:
690 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
692 case CSSM_ALGID_CAST
:
693 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
695 case CSSM_ALGID_ECDSA
:
696 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
699 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%d"), keyAlgValue
);
700 retainString
= false;
704 if (retainString
) CFRetain(stringRef
);
705 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
706 CFRelease(stringRef
);
710 // normal case: attribute contains a string
711 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
712 if (stringRef
== NULL
)
713 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
714 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
715 CFRelease(stringRef
);
720 case kDataRepresentation
:
722 if ((info
[i
].oldItemType
== kSecKeyLabel
) && (list
.attr
[i
].length
== kUUIDStringLength
)) {
723 // It's possible that there could be a string here because the key label may have a UUID
724 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)list
.attr
[i
].data
, list
.attr
[i
].length
, kCFStringEncodingUTF8
, FALSE
);
725 if (stringRef
== NULL
)
726 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
727 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), stringRef
);
728 CFRelease(stringRef
);
731 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*) list
.attr
[i
].data
, list
.attr
[i
].length
);
733 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
734 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dataRef
);
739 case kNumberRepresentation
:
741 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, list
.attr
[i
].data
);
742 if (numberRef
== NULL
)
743 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
744 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), numberRef
);
745 CFRelease(numberRef
);
749 case kBooleanRepresentation
:
751 uint32_t value
= *((uint32_t*)list
.attr
[i
].data
);
752 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
753 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), boolRef
);
757 case kDateRepresentation
:
759 CFDateRef dateRef
= NULL
;
760 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list
.attr
[i
].data
, list
.attr
[i
].length
, &dateRef
);
762 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
763 CFDictionaryAddValue(dictionaryRef
, *(info
[i
].newItemType
), dateRef
);
771 SecKeychainItemFreeContent(&list
, NULL
);
781 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
782 * attributes of item.
785 _CreateAttributesDictionaryFromGenericPasswordItem(
786 CFAllocatorRef allocator
,
787 SecKeychainItemRef item
,
788 CFDictionaryRef
*dictionary
)
790 // do the basic allocations
791 CFMutableDictionaryRef dict
= NULL
;
792 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, item
, dict
);
793 if (result
== errSecSuccess
) // did we complete OK
795 CFDictionaryAddValue(dict
, kSecClass
, kSecClassGenericPassword
);
806 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
807 * attributes of item.
810 _CreateAttributesDictionaryFromCertificateItem(
811 CFAllocatorRef allocator
,
812 SecKeychainItemRef item
,
813 CFDictionaryRef
*dictionary
)
815 // do the basic allocations
816 CFMutableDictionaryRef dict
= NULL
;
817 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gCertificateAttributes
, kNumberOfCertificateAttributes
, item
, dict
);
818 if (result
== errSecSuccess
) // did we complete OK
820 CFDictionaryAddValue(dict
, kSecClass
, kSecClassCertificate
);
825 return errSecSuccess
;
829 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
830 * attributes of item.
833 _CreateAttributesDictionaryFromKeyItem(
834 CFAllocatorRef allocator
,
835 SecKeychainItemRef item
,
836 CFDictionaryRef
*dictionary
)
839 //%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
840 // Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
841 // SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
844 goto error_exit
; // unable to get the attribute info (i.e. database schema)
847 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
849 // do the basic allocations
850 CFMutableDictionaryRef dict
= NULL
;
851 OSStatus result
= _ConvertOldFormatToNewFormat(allocator
, gKeyAttributes
, kNumberOfKeyAttributes
, item
, dict
);
852 if (result
== errSecSuccess
) // did we complete OK
854 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
859 return errSecSuccess
;
862 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
864 SecItemClass itemClass
= 0;
866 SecKeychainAttributeList
*attrList
= NULL
;
867 SecKeychainAttributeInfo
*info
= NULL
;
868 SecKeychainRef keychain
= NULL
;
870 OSStatus status
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
872 goto error_exit
; // item must have an itemClass
877 case kSecInternetPasswordItemClass
:
878 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
880 case kSecGenericPasswordItemClass
:
881 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
883 case 'ashp': /* kSecAppleSharePasswordItemClass */
884 itemID
= CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
;
891 status
= SecKeychainItemCopyKeychain(item
, &keychain
);
893 goto error_exit
; // item must have a keychain, so we can get the attribute info for it
896 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
898 goto error_exit
; // unable to get the attribute info (i.e. database schema)
901 status
= SecKeychainItemCopyAttributesAndData(item
, info
, &itemClass
, &attrList
, NULL
, NULL
);
903 goto error_exit
; // unable to get the attribute info (i.e. database schema)
906 for (ix
= 0; ix
< info
->count
; ++ix
)
908 SecKeychainAttribute
*attribute
= &attrList
->attr
[ix
];
909 if (!attribute
->length
&& !attribute
->data
)
912 UInt32 j
, count
= kNumberOfKeyAttributes
;
913 InternalAttributeListInfo
*intInfo
= NULL
;
914 for (j
=0; j
<count
; j
++) {
915 if (gKeyAttributes
[j
].oldItemType
== info
->tag
[ix
]) {
916 intInfo
= &gKeyAttributes
[j
];
923 switch (intInfo
->itemRepresentation
)
925 case kStringRepresentation
:
927 CFStringRef stringRef
;
928 if (intInfo
->oldItemType
== kSecKeyKeyClass
) {
929 // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
930 UInt32 keyRecordValue
= *((UInt32
*)attribute
->data
);
931 bool retainString
= true;
932 switch (keyRecordValue
) {
933 case CSSM_DL_DB_RECORD_PUBLIC_KEY
:
934 stringRef
= (CFStringRef
) kSecAttrKeyClassPublic
;
936 case CSSM_DL_DB_RECORD_PRIVATE_KEY
:
937 stringRef
= (CFStringRef
) kSecAttrKeyClassPrivate
;
939 case CSSM_DL_DB_RECORD_SYMMETRIC_KEY
:
940 stringRef
= (CFStringRef
) kSecAttrKeyClassSymmetric
;
943 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyRecordValue
);
947 if (retainString
) CFRetain(stringRef
);
948 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
949 CFRelease(stringRef
);
952 else if (intInfo
->oldItemType
== kSecKeyKeyType
) {
953 // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
954 UInt32 keyAlgValue
= *((UInt32
*)attribute
->data
);
955 bool retainString
= true;
956 switch (keyAlgValue
) {
957 case CSSM_ALGID_RSA
:
958 stringRef
= (CFStringRef
) kSecAttrKeyTypeRSA
;
960 case CSSM_ALGID_DSA
:
961 stringRef
= (CFStringRef
) kSecAttrKeyTypeDSA
;
963 case CSSM_ALGID_AES
:
964 stringRef
= (CFStringRef
) kSecAttrKeyTypeAES
;
966 case CSSM_ALGID_DES
:
967 stringRef
= (CFStringRef
) kSecAttrKeyTypeDES
;
969 case CSSM_ALGID_3DES
:
970 stringRef
= (CFStringRef
) kSecAttrKeyType3DES
;
972 case CSSM_ALGID_RC4
:
973 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC4
;
975 case CSSM_ALGID_RC2
:
976 stringRef
= (CFStringRef
) kSecAttrKeyTypeRC2
;
978 case CSSM_ALGID_CAST
:
979 stringRef
= (CFStringRef
) kSecAttrKeyTypeCAST
;
981 case CSSM_ALGID_ECDSA
:
982 stringRef
= (CFStringRef
) kSecAttrKeyTypeEC
;
985 stringRef
= CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%u"), (unsigned int)keyAlgValue
);
986 retainString
= false;
990 if (retainString
) CFRetain(stringRef
);
991 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
992 CFRelease(stringRef
);
996 // normal case: attribute contains a string
997 stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
998 if (stringRef
== NULL
)
999 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1000 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1001 CFRelease(stringRef
);
1006 case kDataRepresentation
:
1008 if ((intInfo
->oldItemType
== kSecKeyLabel
) && (attribute
->length
== kUUIDStringLength
)) {
1009 // It's possible that there could be a string here because the key label may have a UUID
1010 CFStringRef stringRef
= CFStringCreateWithBytes(allocator
, (UInt8
*)attribute
->data
, attribute
->length
, kCFStringEncodingUTF8
, FALSE
);
1011 if (stringRef
== NULL
)
1012 stringRef
= (CFStringRef
) CFRetain(kCFNull
);
1013 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), stringRef
);
1014 CFRelease(stringRef
);
1018 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)attribute
->data
, attribute
->length
);
1019 if (dataRef
== NULL
)
1020 dataRef
= (CFDataRef
) CFRetain(kCFNull
);
1021 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dataRef
);
1026 case kNumberRepresentation
:
1028 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attribute
->data
);
1029 if (numberRef
== NULL
)
1030 numberRef
= (CFNumberRef
) CFRetain(kCFNull
);
1031 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), numberRef
);
1032 CFRelease(numberRef
);
1036 case kBooleanRepresentation
:
1038 UInt32 value
= *((UInt32
*)attribute
->data
);
1039 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1040 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), boolRef
);
1044 case kDateRepresentation
:
1046 //%%% FIXME need to convert from a CSSM date string to a CFDateRef here
1047 CFDateRef dateRef
= NULL
;
1048 if (dateRef
== NULL
)
1049 dateRef
= (CFDateRef
) CFRetain(kCFNull
);
1050 CFDictionaryAddValue(dict
, *(intInfo
->newItemType
), dateRef
);
1057 CFDictionaryAddValue(dict
, kSecClass
, kSecClassKey
);
1062 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
1065 SecKeychainFreeAttributeInfo(info
);
1068 CFRelease(keychain
);
1077 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
1078 * attributes of item.
1081 _CreateAttributesDictionaryFromInternetPasswordItem(
1082 CFAllocatorRef allocator
,
1083 SecKeychainItemRef item
,
1084 CFDictionaryRef
*dictionary
)
1087 SecKeychainAttribute attr
[] = {
1088 { kSecServerItemAttr
, 0, NULL
}, /* [0] server */
1089 { kSecSecurityDomainItemAttr
, 0, NULL
}, /* [1] securityDomain */
1090 { kSecAccountItemAttr
, 0, NULL
}, /* [2] account */
1091 { kSecPathItemAttr
, 0, NULL
}, /* [3] path */
1092 { kSecPortItemAttr
, 0, NULL
}, /* [4] port */
1093 { kSecProtocolItemAttr
, 0, NULL
}, /* [5] protocol */
1094 { kSecAuthenticationTypeItemAttr
, 0, NULL
}, /* [6] authenticationType */
1095 { kSecCommentItemAttr
, 0, NULL
}, /* [7] comment */
1096 { kSecDescriptionItemAttr
, 0, NULL
}, /* [8] description */
1097 { kSecLabelItemAttr
, 0, NULL
}, /* [9] label */
1098 { kSecCreationDateItemAttr
, 0, NULL
}, /* [10] creation date */
1099 { kSecModDateItemAttr
, 0, NULL
}, /* [11] modification date */
1100 { kSecCreatorItemAttr
, 0, NULL
}, /* [12] creator */
1101 { kSecTypeItemAttr
, 0, NULL
}, /* [13] type */
1102 { kSecInvisibleItemAttr
, 0, NULL
}, /* [14] invisible */
1103 { kSecNegativeItemAttr
, 0, NULL
}, /* [15] negative */
1105 SecKeychainAttributeList attrList
= { sizeof(attr
) / sizeof(SecKeychainAttribute
), attr
};
1108 CFTypeRef keys
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1109 CFTypeRef values
[(sizeof(attr
) / sizeof(SecKeychainAttribute
)) + 2];
1113 // copy the item's attributes
1114 status
= SecKeychainItemCopyContent(item
, NULL
, &attrList
, NULL
, NULL
);
1115 require_noerr(status
, SecKeychainItemCopyContent_failed
);
1120 keys
[numValues
] = kSecClass
;
1121 values
[numValues
] = kSecClassInternetPassword
;
1124 // add kSecAttrServer
1125 if ( attrList
.attr
[0].length
> 0 ) {
1126 keys
[numValues
] = kSecAttrServer
;
1127 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[0].data
, attrList
.attr
[0].length
, kCFStringEncodingUTF8
, FALSE
);
1128 if ( values
[numValues
] != NULL
) {
1133 // add kSecAttrSecurityDomain
1134 if ( attrList
.attr
[1].length
> 0 ) {
1135 keys
[numValues
] = kSecAttrSecurityDomain
;
1136 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[1].data
, attrList
.attr
[1].length
, kCFStringEncodingUTF8
, FALSE
);
1137 if ( values
[numValues
] != NULL
) {
1142 // add kSecAttrAccount
1143 if ( attrList
.attr
[2].length
> 0 ) {
1144 keys
[numValues
] = kSecAttrAccount
;
1145 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[2].data
, attrList
.attr
[2].length
, kCFStringEncodingUTF8
, FALSE
);
1146 if ( values
[numValues
] != NULL
) {
1152 if ( attrList
.attr
[3].length
> 0 ) {
1153 keys
[numValues
] = kSecAttrPath
;
1154 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[3].data
, attrList
.attr
[3].length
, kCFStringEncodingUTF8
, FALSE
);
1155 if ( values
[numValues
] != NULL
) {
1161 if ( attrList
.attr
[4].length
> 0 ) {
1162 keys
[numValues
] = kSecAttrPort
;
1163 values
[numValues
] = CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[4].data
);
1164 if ( values
[numValues
] != NULL
) {
1169 // add kSecAttrProtocol
1170 if ( attrList
.attr
[5].length
> 0 ) {
1171 keys
[numValues
] = kSecAttrProtocol
;
1172 values
[numValues
] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType
*)attrList
.attr
[5].data
);
1173 if ( values
[numValues
] != NULL
) {
1174 CFRetain(values
[numValues
]);
1179 // add kSecAttrAuthenticationType
1180 if ( attrList
.attr
[6].length
> 0 ) {
1181 keys
[numValues
] = kSecAttrAuthenticationType
;
1182 values
[numValues
] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType
*)attrList
.attr
[6].data
);
1183 if ( values
[numValues
] != NULL
) {
1184 CFRetain(values
[numValues
]);
1189 // add kSecAttrComment
1190 if ( attrList
.attr
[7].length
> 0 ) {
1191 keys
[numValues
] = kSecAttrComment
;
1192 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[7].data
, attrList
.attr
[7].length
, kCFStringEncodingUTF8
, FALSE
);
1193 if ( values
[numValues
] != NULL
) {
1198 // add kSecAttrDescription
1199 if ( attrList
.attr
[8].length
> 0 ) {
1200 keys
[numValues
] = kSecAttrDescription
;
1201 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[8].data
, attrList
.attr
[8].length
, kCFStringEncodingUTF8
, FALSE
);
1202 if ( values
[numValues
] != NULL
) {
1207 // add kSecAttrLabel
1208 if ( attrList
.attr
[9].length
> 0 ) {
1209 keys
[numValues
] = kSecAttrLabel
;
1210 values
[numValues
] = CFStringCreateWithBytes(allocator
, (UInt8
*)attrList
.attr
[9].data
, attrList
.attr
[9].length
, kCFStringEncodingUTF8
, FALSE
);
1211 if ( values
[numValues
] != NULL
) {
1216 // add kSecAttrCreationDate
1217 if ( attrList
.attr
[10].length
> 0 ) {
1218 CFDateRef creationDate
= NULL
;
1219 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[10].data
, attrList
.attr
[10].length
, &creationDate
);
1220 keys
[numValues
] = kSecAttrCreationDate
;
1221 values
[numValues
] = creationDate
;
1222 if ( values
[numValues
] != NULL
) {
1227 // add kSecAttrModificationDate
1228 if ( attrList
.attr
[11].length
> 0 ) {
1229 CFDateRef modDate
= NULL
;
1230 CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList
.attr
[11].data
, attrList
.attr
[11].length
, &modDate
);
1231 keys
[numValues
] = kSecAttrModificationDate
;
1232 values
[numValues
] = modDate
;
1233 if ( values
[numValues
] != NULL
) {
1238 // add kSecCreatorItemAttr
1239 if ( attrList
.attr
[12].length
> 0 ) {
1240 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[12].data
);
1241 keys
[numValues
] = kSecAttrCreator
;
1242 values
[numValues
] = numberRef
;
1243 if ( values
[numValues
] != NULL
) {
1244 CFRetain(values
[numValues
]);
1249 // add kSecTypeItemAttr
1250 if ( attrList
.attr
[13].length
> 0 ) {
1251 CFNumberRef numberRef
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, attrList
.attr
[13].data
);
1252 keys
[numValues
] = kSecAttrType
;
1253 values
[numValues
] = numberRef
;
1254 if ( values
[numValues
] != NULL
) {
1255 CFRetain(values
[numValues
]);
1260 // add kSecInvisibleItemAttr
1261 if ( attrList
.attr
[14].length
> 0 ) {
1262 uint32_t value
= *((uint32_t*)attrList
.attr
[14].data
);
1263 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1264 keys
[numValues
] = kSecAttrIsInvisible
;
1265 values
[numValues
] = boolRef
;
1266 if ( values
[numValues
] != NULL
) {
1267 CFRetain(values
[numValues
]);
1272 // add kSecNegativeItemAttr
1273 if ( attrList
.attr
[15].length
> 0 ) {
1274 uint32_t value
= *((uint32_t*)attrList
.attr
[15].data
);
1275 CFBooleanRef boolRef
= (value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1276 keys
[numValues
] = kSecAttrIsNegative
;
1277 values
[numValues
] = boolRef
;
1278 if ( values
[numValues
] != NULL
) {
1279 CFRetain(values
[numValues
]);
1284 // create the dictionary
1285 *dictionary
= CFDictionaryCreate(allocator
, keys
, values
, numValues
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1287 // release the values added to the dictionary
1288 for ( index
= 0; index
< numValues
; ++index
)
1290 CFRelease(values
[index
]);
1293 // and free the attributes
1294 (void) SecKeychainItemFreeContent(&attrList
, NULL
);
1296 SecKeychainItemCopyContent_failed
:
1303 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
1304 * attributes of the specified item class and item.
1307 _CreateAttributesDictionaryFromItem(
1308 CFAllocatorRef allocator
,
1309 SecItemClass itemClass
,
1310 SecKeychainItemRef item
,
1311 CFDictionaryRef
*dictionary
)
1315 case kSecInternetPasswordItemClass
:
1316 return _CreateAttributesDictionaryFromInternetPasswordItem(allocator
, item
, dictionary
);
1318 case kSecGenericPasswordItemClass
:
1319 return _CreateAttributesDictionaryFromGenericPasswordItem(allocator
, item
, dictionary
);
1321 case kSecCertificateItemClass
:
1322 return _CreateAttributesDictionaryFromCertificateItem(allocator
, item
, dictionary
);
1324 case kSecPublicKeyItemClass
:
1325 case kSecPrivateKeyItemClass
:
1326 case kSecSymmetricKeyItemClass
:
1327 return _CreateAttributesDictionaryFromKeyItem(allocator
, item
, dictionary
);
1338 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
1339 * by the _CreateSecKeychainAttributeListFromDictionary function.
1343 SecKeychainAttributeList
*attrListPtr
)
1347 if ( attrListPtr
!= NULL
) {
1348 if ( attrListPtr
->attr
!= NULL
) {
1349 // free any attribute data
1350 for ( index
= 0; index
< attrListPtr
->count
; ++index
) {
1351 free(attrListPtr
->attr
[index
].data
);
1353 // free the attribute array
1354 free(attrListPtr
->attr
);
1356 // free the attribute list
1362 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
1363 * attr using the data and tag parameters.
1365 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1366 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1369 _CFDataCreateAttribute(
1371 SecKeychainAttrType tag
,
1372 SecKeychainAttributePtr attr
)
1374 OSStatus status
= errSecSuccess
;
1377 // set the attribute tag
1380 // determine the attribute length
1381 attr
->length
= (UInt32
) CFDataGetLength(data
);
1382 range
= CFRangeMake(0, (CFIndex
)attr
->length
);
1384 // allocate memory for the attribute bytes
1385 attr
->data
= malloc(attr
->length
);
1386 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1388 // get the attribute bytes
1389 CFDataGetBytes(data
, range
, (UInt8
*)attr
->data
);
1397 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
1398 * attr using the string and tag parameters.
1400 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1401 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1404 _CFStringCreateAttribute(
1406 SecKeychainAttrType tag
,
1407 SecKeychainAttributePtr attr
)
1409 OSStatus status
= errSecSuccess
;
1412 // set the attribute tag
1415 // determine the attribute length
1416 range
= CFRangeMake(0, CFStringGetLength(string
));
1417 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, (CFIndex
*)&attr
->length
);
1419 // allocate memory for the attribute bytes
1420 attr
->data
= malloc(attr
->length
);
1421 require_action(attr
->data
!= NULL
, malloc_failed
, status
= errSecBufferTooSmall
);
1423 // get the attribute bytes
1424 CFStringGetBytes(string
, range
, kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*)attr
->data
, attr
->length
, NULL
);
1433 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1434 * from the attribute key/values in attrDictionary.
1436 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1437 * must be freed by the caller with _FreeAttrList()
1440 _CreateSecKeychainGenericPasswordAttributeListFromDictionary(
1441 CFDictionaryRef attrDictionary
,
1442 SecKeychainAttributeList
**attrList
)
1444 return _ConvertNewFormatToOldFormat(NULL
, gGenericPasswordAttributes
, kNumberOfGenericPasswordAttributes
, attrDictionary
, *attrList
);
1449 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
1450 * from the attribute key/values in attrDictionary.
1452 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1453 * must be freed by the caller with _FreeAttrList()
1456 _CreateSecKeychainCertificateAttributeListFromDictionary(
1457 CFDictionaryRef attrDictionary
,
1458 SecKeychainAttributeList
**attrList
)
1460 return _ConvertNewFormatToOldFormat(NULL
, gCertificateAttributes
, kNumberOfCertificateAttributes
, attrDictionary
, *attrList
);
1465 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
1466 * from the attribute key/values in attrDictionary.
1468 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1469 * must be freed by the caller with _FreeAttrList()
1472 _CreateSecKeychainKeyAttributeListFromDictionary(
1473 CFDictionaryRef attrDictionary
,
1474 SecKeychainAttributeList
**attrList
)
1477 //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
1478 return _ConvertNewFormatToOldFormat(NULL
, gKeyAttributes
, kNumberOfKeyAttributes
, attrDictionary
, *attrList
);
1480 // explicitly build attribute list for supported key attributes
1481 // NOTE: this code supports only MaxSecKeyAttributes (15) attributes
1482 const int MaxSecKeyAttributes
= 15;
1486 SecKeychainAttributeList
*attrListPtr
;
1488 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1489 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1491 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeyAttributes
, sizeof(SecKeychainAttribute
));
1492 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1494 // [0] get the kSecKeyKeyClass value
1495 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyClass
, (const void **)&value
) && value
) {
1496 UInt32 keyRecordValue
= 0;
1497 if (CFEqual(kSecAttrKeyClassPublic
, value
))
1498 keyRecordValue
= CSSM_DL_DB_RECORD_PUBLIC_KEY
;
1499 else if (CFEqual(kSecAttrKeyClassPrivate
, value
))
1500 keyRecordValue
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
1501 else if (CFEqual(kSecAttrKeyClassSymmetric
, value
))
1502 keyRecordValue
= CSSM_DL_DB_RECORD_SYMMETRIC_KEY
;
1504 // only use this attribute if we recognize the value!
1505 if (keyRecordValue
!= 0) {
1506 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1507 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1509 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyClass
;
1510 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1511 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyRecordValue
;
1513 ++attrListPtr
->count
;
1517 // [1] get the kSecKeyPrintName string
1518 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) && value
) {
1519 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyPrintName
, &attrListPtr
->attr
[attrListPtr
->count
]);
1520 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1522 ++attrListPtr
->count
;
1525 // [2] get the kSecKeyPermanent boolean
1526 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsPermanent
, (const void **)&value
) && value
) {
1527 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1528 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1530 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyPermanent
;
1531 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1532 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1534 ++attrListPtr
->count
;
1537 // [3] get the kSecKeyLabel string
1538 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationLabel
, (const void **)&value
) && value
) {
1539 if (CFStringGetTypeID() == CFGetTypeID(value
))
1540 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1541 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1542 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyLabel
, &attrListPtr
->attr
[attrListPtr
->count
]);
1544 status
= errSecParam
;
1546 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1548 ++attrListPtr
->count
;
1551 // [4] get the kSecKeyApplicationTag data
1552 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrApplicationTag
, (const void **)&value
) && value
) {
1553 if (CFStringGetTypeID() == CFGetTypeID(value
))
1554 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1555 else if (CFDataGetTypeID() == CFGetTypeID(value
))
1556 status
= _CFDataCreateAttribute((CFDataRef
)value
, kSecKeyApplicationTag
, &attrListPtr
->attr
[attrListPtr
->count
]);
1558 status
= errSecParam
;
1560 require_noerr_quiet(status
, CFDataCreateAttribute_failed
);
1561 ++attrListPtr
->count
;
1564 // [5] get the kSecKeyKeyType number
1565 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeyType
, (const void **)&value
) && value
) {
1566 UInt32 keyAlgValue
= _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType
);
1567 if (keyAlgValue
!= 0) {
1568 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1569 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1571 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeyType
;
1572 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1573 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = keyAlgValue
;
1575 ++attrListPtr
->count
;
1579 // [6] get the kSecKeyKeySizeInBits number
1580 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrKeySizeInBits
, (const void **)&value
) && value
) {
1581 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1582 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1583 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1585 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyKeySizeInBits
;
1586 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1587 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1589 ++attrListPtr
->count
;
1593 // [7] get the kSecKeyEffectiveKeySize number
1594 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrEffectiveKeySize
, (const void **)&value
) && value
) {
1595 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
1596 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1597 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1599 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEffectiveKeySize
;
1600 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1601 CFNumberGetValue((CFNumberRef
)value
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1603 ++attrListPtr
->count
;
1607 // [8] get the kSecKeyEncrypt boolean
1608 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanEncrypt
, (const void **)&value
) && value
) {
1609 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1610 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1611 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1613 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyEncrypt
;
1614 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1615 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1617 ++attrListPtr
->count
;
1621 // [9] get the kSecKeyDecrypt boolean
1622 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDecrypt
, (const void **)&value
) && value
) {
1623 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1624 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1625 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1627 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDecrypt
;
1628 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1629 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1631 ++attrListPtr
->count
;
1635 // [10] get the kSecKeyDerive boolean
1636 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanDerive
, (const void **)&value
) && value
) {
1637 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1638 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1639 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1641 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyDerive
;
1642 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1643 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1645 ++attrListPtr
->count
;
1649 // [11] get the kSecKeySign boolean
1650 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanSign
, (const void **)&value
) && value
) {
1651 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1652 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1653 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1655 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeySign
;
1656 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1657 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1659 ++attrListPtr
->count
;
1663 // [12] get the kSecKeyVerify boolean
1664 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanVerify
, (const void **)&value
) && value
) {
1665 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1666 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1667 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1669 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyVerify
;
1670 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1671 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1673 ++attrListPtr
->count
;
1677 // [13] get the kSecKeyWrap boolean
1678 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanWrap
, (const void **)&value
) && value
) {
1679 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1680 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1681 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1683 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyWrap
;
1684 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1685 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1687 ++attrListPtr
->count
;
1691 // [14] get the kSecKeyUnwrap boolean
1692 if (CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCanUnwrap
, (const void **)&value
) && value
) {
1693 if (CFBooleanGetTypeID() == CFGetTypeID(value
)) {
1694 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1695 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_number_failed
, status
= errSecBufferTooSmall
);
1697 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecKeyUnwrap
;
1698 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1699 *((UInt32
*)attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFEqual(kCFBooleanTrue
, value
)) ? 1 : 0;
1701 ++attrListPtr
->count
;
1705 // return the pointer to the attrList
1706 *attrList
= attrListPtr
;
1708 return ( errSecSuccess
);
1712 malloc_number_failed
:
1713 CFDataCreateAttribute_failed
:
1714 CFStringCreateAttribute_failed
:
1715 malloc_attrPtr_failed
:
1717 // free any attributes
1718 _FreeAttrList(attrListPtr
);
1720 calloc_attrListPtr_failed
:
1722 return ( errSecBufferTooSmall
);
1727 static CFTypeRef
copyNumber(CFTypeRef obj
)
1732 CFTypeID tid
= CFGetTypeID(obj
);
1733 if (tid
== CFNumberGetTypeID())
1739 if (tid
== CFBooleanGetTypeID())
1741 SInt32 value
= CFBooleanGetValue((CFBooleanRef
)obj
);
1742 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1745 if (tid
== CFStringGetTypeID())
1747 SInt32 value
= CFStringGetIntValue((CFStringRef
)obj
);
1748 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
1749 /* If a string converted to an int isn't equal to the int printed as
1750 a string, return a NULL instead. */
1751 if (!CFEqual(t
, obj
))
1757 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
1763 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1764 * from the attribute key/values in attrDictionary.
1766 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1767 * must be freed by the caller with _FreeAttrList()
1770 _CreateSecKeychainInternetPasswordAttributeListFromDictionary(
1771 CFDictionaryRef attrDictionary
,
1772 SecKeychainAttributeList
**attrList
)
1774 // explicitly build attribute list for supported key attributes
1775 // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
1776 const int MaxSecKeychainAttributes
= 14;
1780 SecKeychainAttributeList
*attrListPtr
;
1782 attrListPtr
= (SecKeychainAttributeList
*)calloc(1, sizeof(SecKeychainAttributeList
));
1783 require_action(attrListPtr
!= NULL
, calloc_attrListPtr_failed
, status
= errSecBufferTooSmall
);
1785 attrListPtr
->attr
= (SecKeychainAttribute
*)calloc(MaxSecKeychainAttributes
, sizeof(SecKeychainAttribute
));
1786 require_action(attrListPtr
->attr
!= NULL
, malloc_attrPtr_failed
, status
= errSecBufferTooSmall
);
1789 // [0] get the serverName string
1790 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrServer
, (const void **)&value
) ) {
1791 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecServerItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1792 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1794 ++attrListPtr
->count
;
1797 // [1] get the securityDomain string
1798 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrSecurityDomain
, (const void **)&value
) ) {
1799 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecSecurityDomainItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1800 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1802 ++attrListPtr
->count
;
1805 // [2] get the accountName string
1806 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAccount
, (const void **)&value
) ) {
1807 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecAccountItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1808 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1810 ++attrListPtr
->count
;
1813 // [3] get the path string
1814 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPath
, (const void **)&value
) ) {
1815 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecPathItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1816 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1818 ++attrListPtr
->count
;
1821 // [4] get the port number
1822 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrPort
, (const void **)&value
) ) {
1823 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt16
));
1824 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1826 CFTypeRef num
= copyNumber(value
);
1827 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1828 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecPortItemAttr
;
1829 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt16
);
1830 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt16Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1833 ++attrListPtr
->count
;
1836 // [5] get the protocol
1837 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrProtocol
, (const void **)&value
) ) {
1838 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecProtocolType
));
1839 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_protocol_failed
, status
= errSecBufferTooSmall
);
1841 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecProtocolItemAttr
;
1842 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecProtocolType
);
1843 *(SecProtocolType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecProtocolTypeForSecAttrProtocol(value
);
1845 ++attrListPtr
->count
;
1848 // [6] get the authenticationType
1849 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrAuthenticationType
, (const void **)&value
) ) {
1850 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(SecAuthenticationType
));
1851 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_authenticationType_failed
, status
= errSecBufferTooSmall
);
1853 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecAuthenticationTypeItemAttr
;
1854 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(SecAuthenticationType
);
1855 *(SecAuthenticationType
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = _SecAuthenticationTypeForSecAttrAuthenticationType(value
);
1857 ++attrListPtr
->count
;
1860 // [7] get the comment string
1861 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrComment
, (const void **)&value
) ) {
1862 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecCommentItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1863 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1865 ++attrListPtr
->count
;
1868 // [8] get the description string
1869 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrDescription
, (const void **)&value
) ) {
1870 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecDescriptionItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1871 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1873 ++attrListPtr
->count
;
1876 // [9] get the label string
1877 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrLabel
, (const void **)&value
) ) {
1878 status
= _CFStringCreateAttribute((CFStringRef
)value
, kSecLabelItemAttr
, &attrListPtr
->attr
[attrListPtr
->count
]);
1879 require_noerr_quiet(status
, CFStringCreateAttribute_failed
);
1881 ++attrListPtr
->count
;
1884 // [10] get the creator code
1885 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrCreator
, (const void **)&value
) ) {
1886 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1887 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1889 CFTypeRef num
= copyNumber(value
);
1890 require_action(num
!= NULL
, CFStringCreateAttribute_failed
, status
= errSecParam
);
1891 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecCreatorItemAttr
;
1892 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1893 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1896 ++attrListPtr
->count
;
1899 // [11] get the type code
1900 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrType
, (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
= kSecTypeItemAttr
;
1907 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1908 CFNumberGetValue((CFNumberRef
)num
, kCFNumberSInt32Type
, attrListPtr
->attr
[attrListPtr
->count
].data
);
1911 ++attrListPtr
->count
;
1914 // [12] get the invisible flag
1915 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsInvisible
, (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 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecInvisibleItemAttr
;
1920 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1921 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1923 ++attrListPtr
->count
;
1926 // [13] get the negative flag
1927 if ( CFDictionaryGetValueIfPresent(attrDictionary
, kSecAttrIsNegative
, (const void **)&value
) ) {
1928 attrListPtr
->attr
[attrListPtr
->count
].data
= malloc(sizeof(UInt32
));
1929 require_action(attrListPtr
->attr
[attrListPtr
->count
].data
!= NULL
, malloc_port_failed
, status
= errSecBufferTooSmall
);
1931 attrListPtr
->attr
[attrListPtr
->count
].tag
= kSecNegativeItemAttr
;
1932 attrListPtr
->attr
[attrListPtr
->count
].length
= sizeof(UInt32
);
1933 *(UInt32
*)(attrListPtr
->attr
[attrListPtr
->count
].data
) = (CFBooleanGetValue((CFBooleanRef
)value
)) ? 1 : 0;
1935 ++attrListPtr
->count
;
1938 // return the pointer to the attrList
1939 *attrList
= attrListPtr
;
1941 return ( errSecSuccess
);
1945 malloc_authenticationType_failed
:
1946 malloc_protocol_failed
:
1948 CFStringCreateAttribute_failed
:
1949 malloc_attrPtr_failed
:
1951 // free any attributes
1952 _FreeAttrList(attrListPtr
);
1954 calloc_attrListPtr_failed
:
1956 return ( errSecBufferTooSmall
);
1961 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
1962 * from the attribute key/values in attrDictionary for the specified item class.
1964 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1965 * must be freed by the caller with _FreeAttrList()
1968 _CreateSecKeychainAttributeListFromDictionary(
1969 CFDictionaryRef attrDictionary
,
1970 SecItemClass itemClass
,
1971 SecKeychainAttributeList
**attrList
)
1975 case kSecInternetPasswordItemClass
:
1976 return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1978 case kSecGenericPasswordItemClass
:
1979 return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary
, attrList
);
1981 case kSecCertificateItemClass
:
1982 return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary
, attrList
);
1984 case kSecPublicKeyItemClass
:
1985 case kSecPrivateKeyItemClass
:
1986 case kSecSymmetricKeyItemClass
:
1987 return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary
, attrList
);
1997 * _AppNameFromSecTrustedApplication attempts to pull the name of the
1998 * application/tool from the SecTrustedApplicationRef.
2001 _AppNameFromSecTrustedApplication(
2002 CFAllocatorRef alloc
,
2003 SecTrustedApplicationRef appRef
)
2007 CFDataRef appDataRef
;
2011 // get the data for item's application/tool
2012 status
= SecTrustedApplicationCopyData(appRef
, &appDataRef
);
2013 if ( status
== errSecSuccess
) {
2016 // convert it to a CFString potentially containing the path
2017 path
= CFStringCreateWithCString(NULL
, (char *)CFDataGetBytePtrVoid(appDataRef
), kCFStringEncodingUTF8
);
2018 if ( path
!= NULL
) {
2019 // the path has to start with a "/" and cannot contain "://"
2020 if ( CFStringHasPrefix(path
, CFSTR("/")) && (CFStringFind(path
, CFSTR("://"), 0).location
== kCFNotFound
) ) {
2021 CFRange nameRange
, compRg
;
2023 nameRange
= CFRangeMake(0, CFStringGetLength(path
));
2025 // remove the trailing slashes (if any)
2026 while ( (nameRange
.length
> 0) && (CFStringGetCharacterAtIndex(path
, nameRange
.length
- 1) == '/') ) {
2027 nameRange
.length
--;
2030 if ( nameRange
.length
> 0 ) {
2031 // find last slash and adjust nameRange to be everything after it
2032 if ( CFStringFindWithOptions(path
, CFSTR("/"), nameRange
, kCFCompareBackwards
, &compRg
) ) {
2033 nameRange
.length
= nameRange
.location
+ nameRange
.length
- (compRg
.location
+ 1);
2034 nameRange
.location
= compRg
.location
+ 1;
2037 result
= CFStringCreateWithSubstring(alloc
, path
, nameRange
);
2042 CFRelease(appDataRef
);
2048 /* (This function really belongs in SecIdentity.cpp!)
2050 * Returns the public key item corresponding to the identity, if it exists in
2051 * the same keychain as the private key. Note that the public key might not
2052 * exist in the same keychain (e.g. if the identity was imported via PKCS12),
2053 * in which case it will not be found.
2056 _SecIdentityCopyPublicKey(
2057 SecIdentityRef identityRef
,
2058 SecKeyRef
*publicKeyRef
)
2062 SecKeychainAttribute attr
= { kSecKeyLabel
, 0, NULL
};
2063 SecKeychainAttributeList attrList
= { 1, &attr
};
2064 SecKeychainAttributeList
*keyAttrList
= NULL
;
2065 SecKeychainAttributeInfo
*info
= NULL
;
2066 SecKeychainSearchRef search
= NULL
;
2067 SecKeychainRef keychain
= NULL
;
2068 SecKeychainItemRef privateKey
= NULL
;
2069 SecKeychainItemRef publicKey
= NULL
;
2071 status
= SecIdentityCopyPrivateKey(identityRef
, (SecKeyRef
*)&privateKey
);
2073 goto error_exit
; // identity must have a private key
2075 status
= SecKeychainItemCopyKeychain(privateKey
, &keychain
);
2077 goto error_exit
; // private key must have a keychain, so we can get the attribute info for it
2079 status
= SecKeychainAttributeInfoForItemID(keychain
, kSecPrivateKeyItemClass
, &info
);
2081 goto error_exit
; // unable to get the attribute info (i.e. database schema) for private keys
2083 status
= SecKeychainItemCopyAttributesAndData(privateKey
, info
, NULL
, &keyAttrList
, NULL
, NULL
);
2085 goto error_exit
; // unable to get the key label attribute for the private key
2088 // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
2089 for (count
= 0; count
< keyAttrList
->count
; count
++) {
2090 if (keyAttrList
->attr
[count
].tag
== kSecKeyLabel
) {
2091 attr
.length
= keyAttrList
->attr
[count
].length
;
2092 attr
.data
= keyAttrList
->attr
[count
].data
;
2096 if (!attr
.length
|| !attr
.data
) {
2097 status
= errSecNoSuchAttr
;
2098 goto error_exit
; // the private key didn't have the hash of the public key in its kSecKeyLabel
2100 status
= SecKeychainSearchCreateFromAttributes(keychain
, kSecPublicKeyItemClass
, &attrList
, &search
);
2102 goto error_exit
; // unable to create the search reference
2104 status
= SecKeychainSearchCopyNext(search
, &publicKey
);
2106 goto error_exit
; // unable to find the public key
2110 *publicKeyRef
= (SecKeyRef
)publicKey
;
2112 CFRelease(publicKey
);
2115 if (status
!= errSecSuccess
) {
2117 *publicKeyRef
= NULL
;
2119 CFRelease(publicKey
);
2125 SecKeychainItemFreeAttributesAndData(keyAttrList
, NULL
);
2128 SecKeychainFreeAttributeInfo(info
);
2131 CFRelease(keychain
);
2134 CFRelease(privateKey
);
2141 * Deletes a keychain item if the current application/tool is the only application/tool
2142 * with decrypt access to that keychain item. If more than one application/tool
2143 * has decrypt access to the keychain item, the item is left on the keychain.
2145 * TBD: If more than one app/tool has access to the keychain item, we should remove
2146 * the current app/tool's decrypt access. There's no easy way to do that with
2147 * current keychain APIs without bringing up the security UI.
2150 _SafeSecKeychainItemDelete(
2151 SecKeychainItemRef itemRef
)
2154 SecAccessRef access
= NULL
;
2155 CFArrayRef aclList
= NULL
;
2156 SecACLRef acl
= NULL
;
2157 CFArrayRef appList
= NULL
;
2158 CFStringRef description
= NULL
;
2159 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector
;
2160 CFIndex idx
, count
= 0;
2161 SecTrustedApplicationRef currentAppRef
= NULL
;
2162 CFStringRef itemAppName
= NULL
, currentAppName
= NULL
;
2164 SecItemClass itemClass
= (SecItemClass
)0;
2165 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2166 if (!(itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2167 // only perform the access control safety check on deletion of password credentials;
2168 // if the item is of some other type, delete it normally.
2169 return SecKeychainItemDelete(itemRef
);
2172 // skip access control checking for web form passwords: <rdar://10957301>
2173 // This permits Safari to manage the removal of all web form passwords,
2174 // regardless of whether they are shared by multiple applications.
2175 if (itemClass
== kSecInternetPasswordItemClass
) {
2176 UInt32 tags
[1] = { kSecAuthenticationTypeItemAttr
};
2177 SecKeychainAttributeInfo attrInfo
= { 1, tags
, NULL
};
2178 SecKeychainAttributeList
*attrs
= NULL
;
2179 status
= SecKeychainItemCopyAttributesAndData(itemRef
, &attrInfo
, NULL
, &attrs
, NULL
, NULL
);
2180 if (!status
&& attrs
) {
2181 bool webFormPassword
= (attrs
->attr
[0].length
== 4 && (!memcmp(attrs
->attr
[0].data
, "form", 4)));
2182 SecKeychainItemFreeAttributesAndData(attrs
, NULL
);
2183 if (webFormPassword
) {
2184 return SecKeychainItemDelete(itemRef
);
2189 // copy the access of the keychain item
2190 status
= SecKeychainItemCopyAccess(itemRef
, &access
);
2191 require_noerr(status
, finish
);
2192 require_quiet(access
!= NULL
, finish
);
2194 // copy the decrypt access control lists -- this is what has access to the keychain item
2195 status
= SecAccessCopySelectedACLList(access
, CSSM_ACL_AUTHORIZATION_DECRYPT
, &aclList
);
2196 require_noerr(status
, finish
);
2197 require_quiet(aclList
!= NULL
, finish
);
2199 // get the access control list
2200 acl
= (SecACLRef
)CFArrayGetValueAtIndex(aclList
, 0);
2201 require_quiet(acl
!= NULL
, finish
);
2203 // copy the application list, description, and CSSM prompt selector for a given access control list entry
2204 status
= SecACLCopySimpleContents(acl
, &appList
, &description
, &promptSelector
);
2205 require_noerr(status
, finish
);
2206 require_quiet(appList
!= NULL
, finish
);
2208 // does the calling application/tool have decrypt access to this item?
2209 count
= CFArrayGetCount(appList
);
2210 for ( idx
= 0; idx
< count
; idx
++ ) {
2211 // get SecTrustedApplicationRef for this entry
2212 SecTrustedApplicationRef itemAppRef
= (SecTrustedApplicationRef
)CFArrayGetValueAtIndex(appList
, idx
);
2213 require_quiet(itemAppRef
!= NULL
, finish
);
2215 // copy the name out
2216 CFReleaseSafe(itemAppName
);
2217 itemAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), itemAppRef
);
2218 if (itemAppName
== NULL
) {
2220 * If there is no app name, it's probably because it's not an appname
2221 * in the ACE but an entitlement/info.plist based rule instead;
2222 * just let the caller have it. */
2227 // create SecTrustedApplicationRef for current application/tool
2228 CFReleaseSafe(currentAppRef
);
2229 status
= SecTrustedApplicationCreateFromPath(NULL
, ¤tAppRef
);
2230 require_noerr(status
, finish
);
2231 require_quiet(currentAppRef
!= NULL
, finish
);
2233 // copy the name out
2234 CFReleaseSafe(currentAppName
);
2235 currentAppName
= _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef
), currentAppRef
);
2236 require_quiet(currentAppName
!= NULL
, finish
);
2238 // compare the names to see if we own the decrypt access
2239 // TBD: validation of membership in an application group
2240 if ( CFStringCompare(currentAppName
, itemAppName
, 0) == kCFCompareEqualTo
) {
2248 CFReleaseSafe(currentAppName
);
2249 CFReleaseSafe(itemAppName
);
2250 CFReleaseSafe(currentAppRef
);
2251 CFReleaseSafe(description
);
2252 CFReleaseSafe(appList
);
2253 CFReleaseSafe(aclList
);
2254 CFReleaseSafe(access
);
2256 if ((count
== 0) || (status
== errSecVerifyFailed
)) {
2257 // no "owners" remain in the ACL list (or unable to get ACL)
2258 status
= SecKeychainItemDelete(itemRef
);
2260 // caller is not the "owner" of the item
2261 status
= errSecInvalidOwnerEdit
;
2268 _ReplaceKeychainItem(
2269 SecKeychainItemRef itemToUpdate
,
2270 SecKeychainAttributeList
*changeAttrList
,
2275 SecItemClass itemClass
;
2276 SecKeychainAttributeInfo
*info
= NULL
;
2277 SecKeychainAttributeList
*attrList
= NULL
;
2278 SecKeychainAttributeList newAttrList
= { 0, NULL
};
2279 SecKeychainRef keychain
= NULL
;
2280 SecKeychainItemRef newItem
= NULL
;
2282 int priority
= LOG_DEBUG
;
2283 const char *format
= "ReplaceKeychainItem (%d) error %d";
2285 // get existing item's keychain
2286 status
= SecKeychainItemCopyKeychain(itemToUpdate
, &keychain
);
2287 if (status
) { secitemlog(priority
, format
, 1, (int)status
); }
2288 require_noerr(status
, replace_failed
);
2290 // get attribute info (i.e. database schema) for the item class
2291 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2292 if (status
) { secitemlog(priority
, format
, 2, (int)status
); }
2293 require_noerr(status
, replace_failed
);
2297 case kSecInternetPasswordItemClass
:
2298 itemID
= CSSM_DL_DB_RECORD_INTERNET_PASSWORD
;
2300 case kSecGenericPasswordItemClass
:
2301 itemID
= CSSM_DL_DB_RECORD_GENERIC_PASSWORD
;
2307 status
= SecKeychainAttributeInfoForItemID(keychain
, itemID
, &info
);
2308 if (status
) { secitemlog(priority
, format
, 3, (int)status
); }
2310 // get item's existing attributes (but not data!)
2311 status
= SecKeychainItemCopyAttributesAndData(itemToUpdate
, info
, &itemClass
, &attrList
, NULL
, NULL
);
2312 if (status
) { secitemlog(priority
, format
, 4, (int)status
); }
2313 require(attrList
!= NULL
, replace_failed
);
2315 // move aside the item by changing a primary attribute
2316 // (currently only for passwords)
2317 if (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
) {
2318 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
2319 CFStringRef uuidStr
= (uuid
) ? CFUUIDCreateString(kCFAllocatorDefault
, uuid
) : CFSTR("MOVED");
2320 CFReleaseSafe(uuid
);
2322 CFIndex maxLength
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(uuidStr
), kCFStringEncodingUTF8
) + 1;
2323 char* buffer
= (char*) malloc(maxLength
);
2325 if (CFStringGetCString(uuidStr
, buffer
, maxLength
, kCFStringEncodingUTF8
)) {
2326 UInt32 length
= (UInt32
)strlen(buffer
);
2327 SecKeychainAttribute attrs
[] = { { kSecAccountItemAttr
, length
, (char*)buffer
}, };
2328 SecKeychainAttributeList updateAttrList
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
2329 status
= SecKeychainItemModifyAttributesAndData(itemToUpdate
, &updateAttrList
, 0, NULL
);
2330 if (status
) { secitemlog(priority
, format
, 5, (int)status
); }
2331 if (status
== errSecVerifyFailed
) {
2332 // still unable to change attrs? delete unconditionally here
2333 status
= SecKeychainItemDelete(itemToUpdate
);
2334 if (status
) { secitemlog(priority
, format
, 6, (int)status
); }
2339 CFReleaseSafe(uuidStr
);
2342 require_noerr(status
, replace_failed
);
2344 // make attribute list for new item (the data is still owned by attrList)
2345 newAttrList
.count
= attrList
->count
;
2346 newAttrList
.attr
= (SecKeychainAttribute
*) malloc(sizeof(SecKeychainAttribute
) * attrList
->count
);
2348 for (i
=0, newCount
=0; i
< attrList
->count
; i
++) {
2349 if (attrList
->attr
[i
].length
> 0) {
2350 newAttrList
.attr
[newCount
++] = attrList
->attr
[i
];
2352 // debugging code to log item attributes
2353 SecKeychainAttrType tag
= attrList
->attr
[i
].tag
;
2354 SecKeychainAttrType htag
=(SecKeychainAttrType
)OSSwapConstInt32(tag
);
2355 char tmp
[sizeof(SecKeychainAttrType
) + 1];
2356 char tmpdata
[attrList
->attr
[i
].length
+ 1];
2357 memcpy(tmp
, &htag
, sizeof(SecKeychainAttrType
));
2358 tmp
[sizeof(SecKeychainAttrType
)]=0;
2359 memcpy(tmpdata
, attrList
->attr
[i
].data
, attrList
->attr
[i
].length
);
2360 tmpdata
[attrList
->attr
[i
].length
]=0;
2361 secitemlog(priority
, "item attr '%s' = %d bytes: \"%s\"",
2362 tmp
, (int)attrList
->attr
[i
].length
, tmpdata
);
2366 newAttrList
.count
= newCount
;
2368 // create new item in the same keychain
2369 status
= SecKeychainItemCreateFromContent(itemClass
, &newAttrList
,
2370 (UInt32
)((itemData
) ? CFDataGetLength(itemData
) : 0),
2371 (const void *)((itemData
) ? CFDataGetBytePtr(itemData
) : NULL
),
2372 keychain
, NULL
, &newItem
);
2373 if (status
) { secitemlog(priority
, format
, 7, (int)status
); }
2374 require_noerr(status
, replace_failed
);
2376 // delete the old item unconditionally once new item exists
2377 status
= SecKeychainItemDelete(itemToUpdate
);
2379 // update the new item with changed attributes, if any
2380 status
= (changeAttrList
) ? SecKeychainItemModifyContent(newItem
, changeAttrList
, 0, NULL
) : errSecSuccess
;
2381 if (status
) { secitemlog(priority
, format
, 8, (int)status
); }
2382 if (status
== errSecSuccess
) {
2383 // say the item already exists, because it does now. <rdar://19063674>
2384 status
= errSecDuplicateItem
;
2388 if (newAttrList
.attr
) {
2389 free(newAttrList
.attr
);
2392 SecKeychainItemFreeAttributesAndData(attrList
, NULL
);
2395 SecKeychainFreeAttributeInfo(info
);
2397 CFReleaseSafe(newItem
);
2398 CFReleaseSafe(keychain
);
2404 _UpdateKeychainItem(CFTypeRef item
, CFDictionaryRef changedAttributes
)
2406 // This function updates a single keychain item, which may be specified as
2407 // a reference, persistent reference or attribute dictionary, with the
2408 // attributes provided.
2410 OSStatus status
= errSecSuccess
;
2415 SecItemClass itemClass
;
2416 SecAccessRef access
= NULL
;
2417 SecKeychainAttributeList
*changeAttrList
= NULL
;
2418 SecKeychainItemRef itemToUpdate
= NULL
;
2419 CFDataRef theData
= NULL
;
2420 CFTypeID itemType
= CFGetTypeID(item
);
2422 // validate input item (must be convertible to a SecKeychainItemRef)
2423 if (SecKeychainItemGetTypeID() == itemType
||
2424 SecCertificateGetTypeID() == itemType
||
2425 SecKeyGetTypeID() == itemType
) {
2426 // item is already a reference, retain it
2427 itemToUpdate
= (SecKeychainItemRef
) CFRetain(item
);
2429 else if (CFDataGetTypeID() == itemType
) {
2430 // item is a persistent reference, must convert it
2431 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToUpdate
);
2433 else if (CFDictionaryGetTypeID() == itemType
) {
2434 // item is a dictionary
2435 CFTypeRef value
= NULL
;
2436 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2437 // kSecValueRef value is a SecKeychainItemRef, retain it
2438 itemToUpdate
= (SecKeychainItemRef
) CFRetain(value
);
2440 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2441 // kSecValuePersistentRef value is a persistent reference, must convert it
2442 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToUpdate
);
2445 else if (SecIdentityGetTypeID() == itemType
) {
2446 // item is a certificate + private key; since we can't really change the
2447 // certificate's attributes, assume we want to update the private key
2448 status
= SecIdentityCopyPrivateKey((SecIdentityRef
)item
, (SecKeyRef
*)&itemToUpdate
);
2450 require_action(itemToUpdate
!= NULL
, update_failed
, status
= errSecInvalidItemRef
);
2451 require_noerr(status
, update_failed
);
2453 status
= SecKeychainItemCopyContent(itemToUpdate
, &itemClass
, NULL
, NULL
, NULL
);
2454 require_noerr(status
, update_failed
);
2456 // build changeAttrList from changedAttributes dictionary
2459 case kSecInternetPasswordItemClass
:
2461 status
= _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2462 require_noerr(status
, update_failed
);
2466 case kSecGenericPasswordItemClass
:
2468 status
= _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2469 require_noerr(status
, update_failed
);
2473 case kSecCertificateItemClass
:
2475 status
= _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2476 require_noerr(status
, update_failed
);
2480 case kSecPublicKeyItemClass
:
2481 case kSecPrivateKeyItemClass
:
2482 case kSecSymmetricKeyItemClass
:
2484 status
= _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes
, &changeAttrList
);
2485 require_noerr(status
, update_failed
);
2490 // (if the caller is not updating the password, this value will be NULL)
2491 theData
= (CFDataRef
)CFDictionaryGetValue(changedAttributes
, kSecValueData
);
2492 if (theData
!= NULL
) {
2493 require_action(CFDataGetTypeID() == CFGetTypeID(theData
), update_failed
, status
= errSecParam
);
2496 status
= SecKeychainItemModifyContent(itemToUpdate
,
2497 (changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2498 (theData
!= NULL
) ? (UInt32
)CFDataGetLength(theData
) : 0,
2499 (theData
!= NULL
) ? CFDataGetBytePtrVoid(theData
) : NULL
);
2500 require_noerr(status
, update_failed
);
2502 // one more thing... update access?
2503 if (CFDictionaryGetValueIfPresent(changedAttributes
, kSecAttrAccess
, (const void **)&access
)) {
2504 status
= SecKeychainItemSetAccess(itemToUpdate
, access
);
2508 if (status
== errSecVerifyFailed
&&
2509 (itemClass
== kSecInternetPasswordItemClass
|| itemClass
== kSecGenericPasswordItemClass
)) {
2510 // if we got a cryptographic failure updating a password item, it needs to be replaced
2511 status
= _ReplaceKeychainItem(itemToUpdate
,
2512 (changeAttrList
->count
== 0) ? NULL
: changeAttrList
,
2516 CFRelease(itemToUpdate
);
2517 _FreeAttrList(changeAttrList
);
2522 _DeleteKeychainItem(CFTypeRef item
)
2524 // This function deletes a single keychain item, which may be specified as
2525 // a reference, persistent reference or attribute dictionary. It will not
2526 // delete non-keychain items or aggregate items (such as a SecIdentityRef);
2527 // it is assumed that the caller will pass identity components separately.
2529 OSStatus status
= errSecSuccess
;
2534 SecKeychainItemRef itemToDelete
= NULL
;
2535 CFTypeID itemType
= CFGetTypeID(item
);
2536 if (SecKeychainItemGetTypeID() == itemType
||
2537 SecCertificateGetTypeID() == itemType
||
2538 SecKeyGetTypeID() == itemType
) {
2539 // item is already a reference, retain it
2540 itemToDelete
= (SecKeychainItemRef
) CFRetain(item
);
2542 else if (CFDataGetTypeID() == itemType
) {
2543 // item is a persistent reference, must convert it
2544 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemToDelete
);
2546 else if (CFDictionaryGetTypeID() == itemType
) {
2547 // item is a dictionary
2548 CFTypeRef value
= NULL
;
2549 if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValueRef
, &value
)) {
2550 // kSecValueRef value is a SecKeychainItemRef, retain it
2551 itemToDelete
= (SecKeychainItemRef
) CFRetain(value
);
2553 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef
)item
, kSecValuePersistentRef
, &value
)) {
2554 // kSecValuePersistentRef value is a persistent reference, must convert it
2555 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)value
, &itemToDelete
);
2561 status
= _SafeSecKeychainItemDelete(itemToDelete
);
2563 CFRelease(itemToDelete
);
2570 _DeleteIdentity(SecIdentityRef identity
)
2572 OSStatus status
, result
= errSecSuccess
;
2573 SecKeyRef privateKey
= NULL
;
2574 SecCertificateRef certificate
= NULL
;
2576 status
= SecIdentityCopyPrivateKey(identity
, &privateKey
);
2578 SecKeyRef publicKey
= NULL
;
2579 status
= _SecIdentityCopyPublicKey(identity
, &publicKey
);
2581 status
= _DeleteKeychainItem(publicKey
);
2582 CFRelease(publicKey
);
2584 status
= _DeleteKeychainItem(privateKey
);
2587 if (privateKey
) CFRelease(privateKey
);
2588 if (status
) result
= status
;
2590 status
= SecIdentityCopyCertificate(identity
, &certificate
);
2592 status
= _DeleteKeychainItem(certificate
);
2595 if (certificate
) CFRelease(certificate
);
2596 if (status
) result
= status
;
2602 _UpdateAggregateStatus(OSStatus newStatus
, OSStatus curStatus
, OSStatus baseStatus
)
2604 // This function is used when atomically processing multiple items,
2605 // where an overall error result must be returned for the entire operation.
2606 // When newStatus is something other than errSecSuccess, we want to keep the "most
2607 // interesting" status (which usually will be newStatus, unless curStatus is
2608 // already set; in that case, newStatus can trump curStatus only by being
2609 // something different than baseStatus.)
2611 OSStatus result
= curStatus
;
2613 if (newStatus
!= errSecSuccess
) {
2615 if (curStatus
!= errSecSuccess
) {
2616 result
= (newStatus
!= baseStatus
) ? newStatus
: curStatus
;
2623 _AddDictValueToOtherDict(const void *key
, const void *value
, void *context
)
2625 // CFDictionaryApplierFunction
2626 // This function just takes the given key/value pair,
2627 // and adds it to another dictionary supplied in the context argument.
2629 CFMutableDictionaryRef dict
= *((CFMutableDictionaryRef
*) context
);
2631 CFDictionaryAddValue(dict
, key
, value
);
2635 static CFStringCompareFlags
2636 _StringCompareFlagsFromQuery(CFDictionaryRef query
)
2639 CFStringCompareFlags flags
= 0;
2640 if (!query
) return flags
;
2642 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&value
) ||
2643 CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2644 flags
|= kCFCompareAnchored
;
2646 if (CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&value
))
2647 flags
|= kCFCompareBackwards
;
2649 if (CFDictionaryGetValueIfPresent(query
, kSecMatchCaseInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2650 flags
|= kCFCompareCaseInsensitive
;
2652 if (CFDictionaryGetValueIfPresent(query
, kSecMatchDiacriticInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2653 flags
|= kCFCompareDiacriticInsensitive
;
2655 if (CFDictionaryGetValueIfPresent(query
, kSecMatchWidthInsensitive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2656 flags
|= kCFCompareWidthInsensitive
;
2662 _CssmKeyUsageFromQuery(CFDictionaryRef query
)
2665 uint32 keyUsage
= 0;
2666 if (!query
) return keyUsage
;
2668 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanEncrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2669 keyUsage
|= CSSM_KEYUSE_ENCRYPT
;
2671 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDecrypt
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2672 keyUsage
|= CSSM_KEYUSE_DECRYPT
;
2674 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanSign
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2675 keyUsage
|= CSSM_KEYUSE_SIGN
;
2677 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanVerify
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2678 keyUsage
|= CSSM_KEYUSE_VERIFY
;
2680 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanWrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2681 keyUsage
|= CSSM_KEYUSE_WRAP
;
2683 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanUnwrap
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2684 keyUsage
|= CSSM_KEYUSE_UNWRAP
;
2686 if (CFDictionaryGetValueIfPresent(query
, kSecAttrCanDerive
, (const void **)&value
) && CFEqual(kCFBooleanTrue
, value
))
2687 keyUsage
|= CSSM_KEYUSE_DERIVE
;
2693 _ConvertItemClass(const void* item
, const void* keyClass
, Boolean
*isIdentity
)
2695 SecItemClass itemClass
= (SecItemClass
) 0;
2696 if (isIdentity
) *isIdentity
= false;
2698 if (CFEqual(item
, kSecClassGenericPassword
)) {
2699 itemClass
= kSecGenericPasswordItemClass
;
2701 else if (CFEqual(item
, kSecClassInternetPassword
)) {
2702 itemClass
= kSecInternetPasswordItemClass
;
2704 else if (CFEqual(item
, kSecClassCertificate
)) {
2705 itemClass
= kSecCertificateItemClass
;
2707 else if (CFEqual(item
, kSecClassIdentity
)) {
2708 // will perform a certificate lookup
2709 itemClass
= kSecCertificateItemClass
;
2710 if (isIdentity
) *isIdentity
= true;
2712 else if (CFEqual(item
, kSecClassKey
)) {
2713 // examine second parameter to determine type of key
2714 if (!keyClass
|| CFEqual(keyClass
, kSecAttrKeyClassSymmetric
)) {
2715 itemClass
= kSecSymmetricKeyItemClass
;
2717 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPublic
)) {
2718 itemClass
= kSecPublicKeyItemClass
;
2720 else if (keyClass
&& CFEqual(keyClass
, kSecAttrKeyClassPrivate
)) {
2721 itemClass
= kSecPrivateKeyItemClass
;
2729 _ItemClassFromItemList(CFArrayRef itemList
)
2731 // Given a list of items (standard or persistent references),
2732 // determine whether they all have the same item class. Returns
2733 // the item class, or 0 if multiple classes in list.
2734 SecItemClass result
= 0;
2735 CFIndex index
, count
= (itemList
) ? CFArrayGetCount(itemList
) : 0;
2736 for (index
=0; index
< count
; index
++) {
2737 CFTypeRef item
= (CFTypeRef
) CFArrayGetValueAtIndex(itemList
, index
);
2739 SecKeychainItemRef itemRef
= NULL
;
2741 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
2742 // persistent reference, resolve first
2743 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &itemRef
);
2746 itemRef
= (SecKeychainItemRef
) CFRetain(item
);
2749 SecItemClass itemClass
= 0;
2750 CFTypeID itemTypeID
= CFGetTypeID(itemRef
);
2751 if (itemTypeID
== SecIdentityGetTypeID() || itemTypeID
== SecCertificateGetTypeID()) {
2752 // Identities and certificates have the same underlying item class
2753 itemClass
= kSecCertificateItemClass
;
2755 else if (itemTypeID
== SecKeychainItemGetTypeID()) {
2756 // Reference to item in a keychain
2757 status
= SecKeychainItemCopyAttributesAndData(itemRef
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
2759 else if (itemTypeID
== SecKeyGetTypeID()) {
2760 // SecKey that isn't stored in a keychain
2761 // %%% will need to change this code when SecKey is no longer CSSM-based %%%
2762 const CSSM_KEY
*cssmKey
;
2763 status
= SecKeyGetCSSMKey((SecKeyRef
)itemRef
, &cssmKey
);
2764 if (status
== errSecSuccess
) {
2765 if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PUBLIC_KEY
)
2766 itemClass
= kSecPublicKeyItemClass
;
2767 else if (cssmKey
->KeyHeader
.KeyClass
== CSSM_KEYCLASS_PRIVATE_KEY
)
2768 itemClass
= kSecPrivateKeyItemClass
;
2770 itemClass
= kSecSymmetricKeyItemClass
;
2774 if (itemClass
!= 0) {
2775 if (result
!= 0 && result
!= itemClass
) {
2776 return 0; // different item classes in list; bail out
2786 // SecItemParams contains a validated set of input parameters, as well as a
2787 // search reference and attribute list built from those parameters. It is
2788 // designed to be allocated with _CreateSecItemParamsFromDictionary, and
2789 // freed with _FreeSecItemParams.
2791 struct SecItemParams
{
2792 CFDictionaryRef query
; // caller-supplied query
2793 int numResultTypes
; // number of result types requested
2794 int maxMatches
; // max number of matches to return
2795 uint32 keyUsage
; // key usage(s) requested
2796 Boolean returningAttributes
; // true if returning attributes dictionary
2797 Boolean returningData
; // true if returning item's data
2798 Boolean returningRef
; // true if returning item reference
2799 Boolean returningPersistentRef
; // true if returing a persistent reference
2800 Boolean returnAllMatches
; // true if we should return all matches
2801 Boolean returnIdentity
; // true if we are returning a SecIdentityRef
2802 Boolean trustedOnly
; // true if we only return trusted certs
2803 Boolean issuerAndSNToMatch
; // true if both issuer and SN were provided
2804 SecItemClass itemClass
; // item class for this query
2805 SecPolicyRef policy
; // value for kSecMatchPolicy (may be NULL)
2806 SecKeychainRef keychain
; // value for kSecUseKeychain (may be NULL)
2807 CFArrayRef useItems
; // value for kSecUseItemList (may be NULL)
2808 CFArrayRef itemList
; // value for kSecMatchItemList (may be NULL)
2809 CFTypeRef searchList
; // value for kSecMatchSearchList (may be NULL)
2810 CFTypeRef matchLimit
; // value for kSecMatchLimit (may be NULL)
2811 CFTypeRef emailAddrToMatch
; // value for kSecMatchEmailAddressIfPresent (may be NULL)
2812 CFTypeRef validOnDate
; // value for kSecMatchValidOnDate (may be NULL)
2813 CFTypeRef keyClass
; // value for kSecAttrKeyClass (may be NULL)
2814 CFTypeRef service
; // value for kSecAttrService (may be NULL)
2815 CFTypeRef issuer
; // value for kSecAttrIssuer (may be NULL)
2816 CFTypeRef serialNumber
; // value for kSecAttrSerialNumber (may be NULL)
2817 CFTypeRef search
; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2818 CFTypeRef assumedKeyClass
; // if no kSecAttrKeyClass provided, holds the current class we're searching for
2819 CFIndex itemListIndex
; // if no search reference but we have itemList, holds index of next item to return
2820 SecKeychainAttributeList
*attrList
; // attribute list for this query
2821 SecAccessRef access
; // access reference (for SecItemAdd only, not used to find items)
2822 CFDataRef itemData
; // item data (for SecItemAdd only, not used to find items)
2823 CFTypeRef itemRef
; // item reference (to find, add, update or delete, depending on context)
2824 SecIdentityRef identityRef
; // identity reference (input as kSecValueRef)
2825 CFDataRef itemPersistentRef
; // item persistent reference (to find, add, update or delete, depending on context)
2826 Boolean isPCSItem
; // true if this query is for a Protected Cloud Storage item
2830 _ValidateDictionaryEntry(CFDictionaryRef dict
, CFTypeRef key
, const void **value
, CFTypeID expectedTypeID
, CFTypeID altTypeID
)
2832 if (!dict
|| !key
|| !value
|| !expectedTypeID
)
2835 if (!CFDictionaryGetValueIfPresent(dict
, key
, value
)) {
2836 // value was not provided for this key (not an error!)
2839 else if (!(*value
)) {
2840 // provided value is NULL (also not an error!)
2841 return errSecSuccess
;
2844 CFTypeID actualTypeID
= CFGetTypeID(*value
);
2845 if (!((expectedTypeID
== actualTypeID
) || (altTypeID
&& altTypeID
== actualTypeID
))) {
2846 // provided value does not have the expected (or alternate) CF type ID
2847 if ((expectedTypeID
== SecKeychainItemGetTypeID()) &&
2848 (actualTypeID
== SecKeyGetTypeID() || actualTypeID
== SecCertificateGetTypeID())) {
2849 // provided value is a "floating" reference which is not yet in a keychain
2851 return errSecSuccess
;
2853 return errSecItemInvalidValue
;
2856 // provided value is OK; retain it
2860 return errSecSuccess
;
2864 _EnsureUserDefaultKeychainIsSearched(SecItemParams
*itemParams
)
2867 CFArrayRef tmpList
= (CFArrayRef
) itemParams
->searchList
;
2869 // search list exists; make it mutable
2870 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2873 // no search list; start with default list
2874 status
= SecKeychainCopySearchList(&tmpList
);
2875 if (!status
&& tmpList
) {
2876 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpList
);
2880 itemParams
->searchList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2884 SecKeychainRef userKeychain
= NULL
;
2885 status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2886 if (!status
&& userKeychain
) {
2887 if (!CFArrayContainsValue((CFArrayRef
)itemParams
->searchList
,
2888 CFRangeMake(0, CFArrayGetCount((CFArrayRef
)itemParams
->searchList
)), userKeychain
)) {
2889 // user's default keychain isn't currently in the search list, so append it
2890 CFArrayAppendValue((CFMutableArrayRef
)itemParams
->searchList
, userKeychain
);
2892 CFRelease(userKeychain
);
2897 _EnsureUserDefaultKeychainIsTargeted(SecItemParams
*itemParams
)
2899 if (itemParams
->keychain
) {
2900 return; // keychain is already explicitly specified, assume it's correct
2902 SecKeychainRef userKeychain
= NULL
;
2903 OSStatus status
= SecKeychainCopyDomainDefault(kSecPreferencesDomainUser
, &userKeychain
);
2904 if (!status
&& userKeychain
) {
2905 itemParams
->keychain
= userKeychain
;
2910 _FreeSecItemParams(SecItemParams
*itemParams
)
2915 if (itemParams
->query
) CFRelease(itemParams
->query
);
2916 if (itemParams
->policy
) CFRelease(itemParams
->policy
);
2917 if (itemParams
->keychain
) CFRelease(itemParams
->keychain
);
2918 if (itemParams
->useItems
) CFRelease(itemParams
->useItems
);
2919 if (itemParams
->itemList
) CFRelease(itemParams
->itemList
);
2920 if (itemParams
->searchList
) CFRelease(itemParams
->searchList
);
2921 if (itemParams
->matchLimit
) CFRelease(itemParams
->matchLimit
);
2922 if (itemParams
->emailAddrToMatch
) CFRelease(itemParams
->emailAddrToMatch
);
2923 if (itemParams
->validOnDate
) CFRelease(itemParams
->validOnDate
);
2924 if (itemParams
->keyClass
) CFRelease(itemParams
->keyClass
);
2925 if (itemParams
->service
) CFRelease(itemParams
->service
);
2926 if (itemParams
->issuer
) CFRelease(itemParams
->issuer
);
2927 if (itemParams
->serialNumber
) CFRelease(itemParams
->serialNumber
);
2928 if (itemParams
->search
) CFRelease(itemParams
->search
);
2929 if (itemParams
->access
) CFRelease(itemParams
->access
);
2930 if (itemParams
->itemData
) CFRelease(itemParams
->itemData
);
2931 if (itemParams
->itemRef
) CFRelease(itemParams
->itemRef
);
2932 if (itemParams
->identityRef
) CFRelease(itemParams
->identityRef
);
2933 if (itemParams
->itemPersistentRef
) CFRelease(itemParams
->itemPersistentRef
);
2935 _FreeAttrList(itemParams
->attrList
);
2940 static SecItemParams
*
2941 _CreateSecItemParamsFromDictionary(CFDictionaryRef dict
, OSStatus
*error
)
2944 CFTypeRef value
= NULL
;
2945 SecItemParams
*itemParams
= (SecItemParams
*) malloc(sizeof(SecItemParams
));
2947 require_action(itemParams
!= NULL
, error_exit
, status
= errSecAllocate
);
2948 require_action(dict
&& (CFDictionaryGetTypeID() == CFGetTypeID(dict
)), error_exit
, status
= errSecParam
);
2950 memset(itemParams
, 0, sizeof(SecItemParams
));
2951 itemParams
->query
= (CFDictionaryRef
) CFRetain(dict
);
2953 // validate input search parameters
2954 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchPolicy
, (const void **)&itemParams
->policy
, SecPolicyGetTypeID(), NULL
), error_exit
);
2955 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchSearchList
, (const void **)&itemParams
->searchList
, CFArrayGetTypeID(), NULL
), error_exit
);
2956 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchItemList
, (const void **)&itemParams
->itemList
, CFArrayGetTypeID(), NULL
), error_exit
);
2957 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchEmailAddressIfPresent
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
2958 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchValidOnDate
, (const void **)&itemParams
->validOnDate
, CFDateGetTypeID(), CFNullGetTypeID()), error_exit
);
2959 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecMatchLimit
, (const void **)&itemParams
->matchLimit
, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit
);
2961 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseItemList
, (const void **)&itemParams
->useItems
, CFArrayGetTypeID(), NULL
), error_exit
);
2962 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecUseKeychain
, (const void **)&itemParams
->keychain
, SecKeychainGetTypeID(), NULL
), error_exit
);
2964 // validate a subset of input attributes (used to create an appropriate search reference)
2965 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrIssuer
, (const void **)&itemParams
->issuer
, CFDataGetTypeID(), NULL
), error_exit
);
2966 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrSerialNumber
, (const void **)&itemParams
->serialNumber
, CFDataGetTypeID(), NULL
), error_exit
);
2967 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrService
, (const void **)&itemParams
->service
, CFStringGetTypeID(), NULL
), error_exit
);
2968 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrKeyClass
, (const void **)&itemParams
->keyClass
, CFStringGetTypeID(), NULL
), error_exit
);
2970 if (itemParams
->service
&& CFStringHasPrefix((CFStringRef
)itemParams
->service
, CFSTR("ProtectedCloudStorage"))) {
2971 itemParams
->isPCSItem
= true;
2972 if (!SecItemSynchronizable(dict
)) {
2973 _EnsureUserDefaultKeychainIsSearched(itemParams
); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
2974 _EnsureUserDefaultKeychainIsTargeted(itemParams
); // for SecItemAdd
2978 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
2979 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueData
, (const void **)&itemParams
->itemData
, CFDataGetTypeID(), CFStringGetTypeID()), error_exit
);
2981 // validate item references
2982 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValueRef
, (const void **)&itemParams
->itemRef
, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit
);
2983 if (itemParams
->itemRef
&& (CFGetTypeID(itemParams
->itemRef
) == SecIdentityGetTypeID())) {
2984 itemParams
->identityRef
= (SecIdentityRef
)itemParams
->itemRef
;
2985 itemParams
->itemRef
= NULL
;
2986 SecIdentityCopyCertificate(itemParams
->identityRef
, (SecCertificateRef
*)&itemParams
->itemRef
);
2988 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecValuePersistentRef
, (const void **)&itemParams
->itemPersistentRef
, CFDataGetTypeID(), NULL
), error_exit
);
2989 if (itemParams
->itemRef
|| itemParams
->itemPersistentRef
) {
2990 // Caller is trying to add or find an item by reference.
2991 // The supported method for doing that is to provide a kSecUseItemList array
2992 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
2993 // so add the item reference to those arrays here.
2994 if (itemParams
->useItems
) {
2995 CFArrayRef tmpItems
= itemParams
->useItems
;
2996 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
2997 CFRelease(tmpItems
);
2999 itemParams
->useItems
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3001 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemRef
);
3002 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->useItems
, itemParams
->itemPersistentRef
);
3004 if (itemParams
->itemList
) {
3005 CFArrayRef tmpItems
= itemParams
->itemList
;
3006 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, tmpItems
);
3007 CFRelease(tmpItems
);
3009 itemParams
->itemList
= (CFArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
3011 if (itemParams
->itemRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemRef
);
3012 if (itemParams
->itemPersistentRef
) CFArrayAppendValue((CFMutableArrayRef
)itemParams
->itemList
, itemParams
->itemPersistentRef
);
3015 // must have an explicit item class, unless one of the following is true:
3016 // - we have an item list to add or search (kSecUseItemList)
3017 // - we have an item reference or persistent reference for the thing we want to look up
3018 // Note that both of these cases will set itemParams->useItems.
3019 // If we have an item list to match (kSecMatchItemList), that still requires an item class,
3020 // so we can perform a search and see if the results match items in the list.
3022 if (!CFDictionaryGetValueIfPresent(dict
, kSecClass
, (const void**) &value
) && !itemParams
->useItems
) {
3023 require_action(false, error_exit
, status
= errSecItemClassMissing
);
3026 itemParams
->itemClass
= _ConvertItemClass(value
, itemParams
->keyClass
, &itemParams
->returnIdentity
);
3027 if (itemParams
->itemClass
== kSecSymmetricKeyItemClass
&& !itemParams
->keyClass
) {
3028 // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
3029 itemParams
->itemClass
= kSecSymmetricKeyItemClass
;
3030 itemParams
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3032 require_action(!(itemParams
->itemClass
== 0 && !itemParams
->useItems
), error_exit
, status
= errSecItemClassMissing
);
3035 itemParams
->keyUsage
= _CssmKeyUsageFromQuery(dict
);
3036 itemParams
->trustedOnly
= CFDictionaryGetValueIfPresent(dict
, kSecMatchTrustedOnly
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3037 itemParams
->issuerAndSNToMatch
= (itemParams
->issuer
!= NULL
&& itemParams
->serialNumber
!= NULL
);
3039 // other input attributes, used for SecItemAdd but not for finding items
3040 require_noerr(status
= _ValidateDictionaryEntry(dict
, kSecAttrAccess
, (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3041 if (itemParams
->access
== NULL
) {
3042 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3043 require_noerr(status
= _ValidateDictionaryEntry(dict
, CFSTR("kSecAttrAccess"), (const void **)&itemParams
->access
, SecAccessGetTypeID(), NULL
), error_exit
);
3046 // determine how to return the result
3047 itemParams
->numResultTypes
= 0;
3048 itemParams
->returningRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3049 if (itemParams
->returningRef
) ++itemParams
->numResultTypes
;
3050 itemParams
->returningPersistentRef
= CFDictionaryGetValueIfPresent(dict
, kSecReturnPersistentRef
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3051 if (itemParams
->returningPersistentRef
) ++itemParams
->numResultTypes
;
3052 itemParams
->returningAttributes
= CFDictionaryGetValueIfPresent(dict
, kSecReturnAttributes
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3053 if (itemParams
->returningAttributes
) ++itemParams
->numResultTypes
;
3054 itemParams
->returningData
= CFDictionaryGetValueIfPresent(dict
, kSecReturnData
, (const void **)&value
) && value
&& CFEqual(kCFBooleanTrue
, value
);
3055 if (itemParams
->returningData
) ++itemParams
->numResultTypes
;
3057 // default is kSecReturnRef if no result types were specified
3058 if (!itemParams
->numResultTypes
) {
3059 itemParams
->returningRef
= TRUE
;
3060 itemParams
->numResultTypes
= 1;
3063 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3064 itemParams
->maxMatches
= 1;
3065 itemParams
->returnAllMatches
= FALSE
;
3066 if (itemParams
->matchLimit
) {
3067 if (CFStringGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3068 itemParams
->returnAllMatches
= CFEqual(kSecMatchLimitAll
, itemParams
->matchLimit
);
3070 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams
->matchLimit
)) {
3071 CFNumberGetValue((CFNumberRef
)itemParams
->matchLimit
, kCFNumberIntType
, &itemParams
->maxMatches
);
3072 require_action(!(itemParams
->maxMatches
< 0), error_exit
, status
= errSecMatchLimitUnsupported
);
3075 if (itemParams
->returnAllMatches
) {
3076 itemParams
->maxMatches
= INT32_MAX
;
3077 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3078 if ((itemParams
->itemClass
==kSecInternetPasswordItemClass
|| itemParams
->itemClass
==kSecGenericPasswordItemClass
) && itemParams
->returningData
)
3079 status
= errSecReturnDataUnsupported
;
3080 require_noerr(status
, error_exit
);
3083 // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference
3084 if (itemParams
->useItems
) {
3085 if (itemParams
->itemClass
== 0) {
3086 itemParams
->itemClass
= _ItemClassFromItemList(itemParams
->useItems
);
3088 status
= errSecSuccess
;
3089 goto error_exit
; // all done here
3092 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3093 require_noerr(status
= _CreateSecKeychainAttributeListFromDictionary(dict
, itemParams
->itemClass
, &itemParams
->attrList
), error_exit
);
3095 // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
3096 if(itemParams
->policy
) {
3097 CFDictionaryRef policyDict
= SecPolicyCopyProperties(itemParams
->policy
);
3098 CFStringRef oidStr
= (CFStringRef
) CFDictionaryGetValue(policyDict
, kSecPolicyOid
);
3099 if(oidStr
&& CFStringCompare(kSecPolicyAppleSMIME
,oidStr
,0) == 0) {
3100 require_noerr(status
= _ValidateDictionaryEntry(policyDict
, kSecPolicyName
, (const void **)&itemParams
->emailAddrToMatch
, CFStringGetTypeID(), NULL
), error_exit
);
3102 CFRelease(policyDict
);
3105 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3106 if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->emailAddrToMatch
) {
3107 // searching for certificates by email address
3108 char *nameBuf
= (char*)malloc(MAXPATHLEN
);
3110 status
= errSecAllocate
;
3112 else if (CFStringGetCString((CFStringRef
)itemParams
->emailAddrToMatch
, nameBuf
, (CFIndex
)MAXPATHLEN
-1, kCFStringEncodingUTF8
)) {
3113 status
= SecKeychainSearchCreateForCertificateByEmail(itemParams
->searchList
, (const char *)nameBuf
, (SecKeychainSearchRef
*)&itemParams
->search
);
3116 status
= errSecItemInvalidValue
;
3118 if (nameBuf
) free(nameBuf
);
3120 else if ((itemParams
->itemClass
== kSecCertificateItemClass
) && itemParams
->issuerAndSNToMatch
) {
3121 // searching for certificates by issuer and serial number
3122 status
= SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams
->searchList
,
3123 (CFDataRef
)itemParams
->issuer
,
3124 (CFDataRef
)itemParams
->serialNumber
,
3125 (SecKeychainSearchRef
*)&itemParams
->search
);
3127 else if (itemParams
->returnIdentity
&& itemParams
->policy
) {
3128 // searching for identities by policy
3129 status
= SecIdentitySearchCreateWithPolicy(itemParams
->policy
,
3130 (CFStringRef
)itemParams
->service
,
3131 itemParams
->keyUsage
,
3132 itemParams
->searchList
,
3133 itemParams
->trustedOnly
,
3134 (SecIdentitySearchRef
*)&itemParams
->search
);
3136 else if (itemParams
->returnIdentity
) {
3137 // searching for identities
3138 status
= SecIdentitySearchCreate(itemParams
->searchList
,
3139 itemParams
->keyUsage
,
3140 (SecIdentitySearchRef
*)&itemParams
->search
);
3143 // normal keychain item search
3144 status
= SecKeychainSearchCreateFromAttributes(itemParams
->searchList
,
3145 itemParams
->itemClass
,
3146 (itemParams
->attrList
->count
== 0) ? NULL
: itemParams
->attrList
,
3147 (SecKeychainSearchRef
*)&itemParams
->search
);
3152 _FreeSecItemParams(itemParams
);
3165 SecKeychainRef keychainRef
,
3166 SecAccessRef accessRef
,
3167 SecKeychainAttributeList
*attrList
,
3168 SecKeychainItemRef
*outItemRef
)
3172 // We must specify the access, since a free-floating key won't have one yet by default
3173 SecPointer
<Access
> access
;
3175 access
= Access::required(accessRef
);
3178 CFStringRef descriptor
= NULL
;
3180 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
3181 SecKeychainAttribute attr
= attrList
->attr
[index
];
3182 if (attr
.tag
== kSecKeyPrintName
) {
3183 descriptor
= CFStringCreateWithBytes(NULL
, (const UInt8
*)attr
.data
, attr
.length
, kCFStringEncodingUTF8
, FALSE
);
3188 if (descriptor
== NULL
) {
3189 descriptor
= (CFStringRef
) CFRetain(CFSTR("<unknown>"));
3191 access
= new Access(cfString(descriptor
));
3192 CFRelease(descriptor
);
3195 KeyItem
*key
= KeyItem::required(keyRef
);
3196 Item item
= key
->importTo(Keychain::optional(keychainRef
), access
, attrList
);
3198 *outItemRef
= item
->handle();
3205 _CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
)
3207 /* Check for ignorable status codes in leaf certificate's evidence */
3208 Boolean result
= true;
3210 for (i
=0; i
< evidence
->NumStatusCodes
; i
++) {
3211 CSSM_RETURN scode
= evidence
->StatusCodes
[i
];
3212 if (scode
== CSSMERR_APPLETP_INVALID_CA
) {
3213 // the TP has rejected this CA cert because it's in the leaf position
3216 else if (ignorableRevocationStatusCode(scode
)) {
3229 _FilterWithPolicy(SecPolicyRef policy
, CFDateRef date
, SecCertificateRef cert
)
3231 CFDictionaryRef props
= NULL
;
3232 CFArrayRef keychains
= NULL
;
3233 CFArrayRef anchors
= NULL
;
3234 CFArrayRef certs
= NULL
;
3235 CFArrayRef chain
= NULL
;
3236 SecTrustRef trust
= NULL
;
3238 SecTrustResultType trustResult
;
3239 CSSM_TP_APPLE_EVIDENCE_INFO
*evidence
= NULL
;
3240 Boolean needChain
= false;
3242 if (!policy
|| !cert
) return errSecParam
;
3244 certs
= CFArrayCreate(NULL
, (const void **)&cert
, (CFIndex
)1, &kCFTypeArrayCallBacks
);
3245 status
= SecTrustCreateWithCertificates(certs
, policy
, &trust
);
3246 if(status
) goto cleanup
;
3248 /* Set evaluation date, if specified (otherwise current date is implied) */
3249 if (date
&& (CFGetTypeID(date
) == CFDateGetTypeID())) {
3250 status
= SecTrustSetVerifyDate(trust
, date
);
3251 if(status
) goto cleanup
;
3254 /* Check whether this is the X509 Basic policy, which means chain building */
3255 props
= SecPolicyCopyProperties(policy
);
3257 CFTypeRef oid
= (CFTypeRef
) CFDictionaryGetValue(props
, kSecPolicyOid
);
3258 if (oid
&& CFEqual(oid
, kSecPolicyAppleX509Basic
)) {
3264 /* To make the evaluation as lightweight as possible, specify an empty array
3265 * of keychains which will be searched for certificates.
3267 keychains
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
3268 status
= SecTrustSetKeychains(trust
, keychains
);
3269 if(status
) goto cleanup
;
3271 /* To make the evaluation as lightweight as possible, specify an empty array
3272 * of trusted anchors.
3274 anchors
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
3275 status
= SecTrustSetAnchorCertificates(trust
, anchors
);
3276 if(status
) goto cleanup
;
3279 /* All parameters are locked and loaded, ready to evaluate! */
3280 status
= SecTrustEvaluate(trust
, &trustResult
);
3281 if(status
) goto cleanup
;
3283 /* If we didn't provide trust anchors or a way to look for them,
3284 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
3285 * However, we can tell whether the policy evaluation succeeded by
3286 * looking at the per-cert status codes in the returned evidence.
3288 status
= SecTrustGetResult(trust
, &trustResult
, &chain
, &evidence
);
3289 if(status
) goto cleanup
;
3291 if (!(trustResult
== kSecTrustResultProceed
||
3292 trustResult
== kSecTrustResultUnspecified
||
3293 trustResult
== kSecTrustResultRecoverableTrustFailure
)) {
3294 /* The evaluation failed in a non-recoverable way */
3295 status
= errSecCertificateCannotOperate
;
3299 /* If there are no per-cert policy status codes,
3300 * and the cert has not expired, consider it valid for the policy.
3304 (void)SecTrustGetCssmResultCode(trust
, &status
);
3306 if((evidence
!= NULL
) && _CanIgnoreLeafStatusCodes(evidence
) &&
3307 ((evidence
[0].StatusBits
& CSSM_CERT_STATUS_EXPIRED
) == 0) &&
3308 ((evidence
[0].StatusBits
& CSSM_CERT_STATUS_NOT_VALID_YET
) == 0)) {
3309 status
= errSecSuccess
;
3313 status
= errSecCertificateCannotOperate
;
3317 if(props
) CFRelease(props
);
3318 if(chain
) CFRelease(chain
);
3319 if(anchors
) CFRelease(anchors
);
3320 if(keychains
) CFRelease(keychains
);
3321 if(certs
) CFRelease(certs
);
3322 if(trust
) CFRelease(trust
);
3328 _FilterWithDate(CFTypeRef validOnDate
, SecCertificateRef cert
)
3330 if (!validOnDate
|| !cert
) return errSecParam
;
3332 CFAbsoluteTime at
, nb
, na
;
3333 if (CFGetTypeID(validOnDate
) == CFDateGetTypeID())
3334 at
= CFDateGetAbsoluteTime((CFDateRef
)validOnDate
);
3336 at
= CFAbsoluteTimeGetCurrent();
3338 OSStatus status
= errSecSuccess
;
3339 nb
= SecCertificateNotValidBefore(cert
);
3340 na
= SecCertificateNotValidAfter(cert
);
3342 if (nb
== 0 || na
== 0 || nb
== na
)
3343 status
= errSecCertificateCannotOperate
;
3345 status
= errSecCertificateNotValidYet
;
3347 status
= errSecCertificateExpired
;
3353 _FilterWithTrust(Boolean trustedOnly
, SecCertificateRef cert
)
3355 if (!cert
) return errSecParam
;
3356 if (!trustedOnly
) return errSecSuccess
;
3358 CFArrayRef certArray
= CFArrayCreate(NULL
, (const void**)&cert
, 1, &kCFTypeArrayCallBacks
);
3359 SecPolicyRef policy
= SecPolicyCreateWithOID(kSecPolicyAppleX509Basic
);
3360 OSStatus status
= (policy
== NULL
) ? errSecPolicyNotFound
: errSecSuccess
;
3363 SecTrustRef trust
= NULL
;
3364 status
= SecTrustCreateWithCertificates(certArray
, policy
, &trust
);
3366 SecTrustResultType trustResult
;
3367 status
= SecTrustEvaluate(trust
, &trustResult
);
3369 if (!(trustResult
== kSecTrustResultProceed
|| trustResult
== kSecTrustResultUnspecified
)) {
3370 status
= (trustResult
== kSecTrustResultDeny
) ? errSecTrustSettingDeny
: errSecNotTrusted
;
3378 CFRelease(certArray
);
3384 static SecKeychainItemRef
3385 CopyResolvedKeychainItem(CFTypeRef item
)
3387 SecKeychainItemRef kcItem
= NULL
;
3390 if (CFGetTypeID(item
) == CFDataGetTypeID()) {
3391 // persistent reference, resolve first
3392 status
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)item
, &kcItem
);
3396 kcItem
= (SecKeychainItemRef
) CFRetain(item
);
3399 // ask for the item's class:
3400 // will return an error if the item has been deleted
3401 SecItemClass itemClass
;
3402 SecKeychainItemRef certRef
= NULL
;
3403 if (CFGetTypeID(kcItem
) == SecIdentityGetTypeID()) {
3404 status
= SecIdentityCopyCertificate((SecIdentityRef
)kcItem
, (SecCertificateRef
*)&certRef
);
3407 // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef
3408 itemClass
= kSecCertificateItemClass
;
3411 status
= SecKeychainItemCopyAttributesAndData(kcItem
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3426 UpdateKeychainSearchAndCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3428 // This function refreshes the search parameters in the specific case where
3429 // the caller is searching for kSecClassKey items but did not provide the
3430 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3431 // we must perform separate searches to obtain all results.
3433 OSStatus status
= errSecItemNotFound
;
3434 if (!params
|| !params
->assumedKeyClass
|| !params
->query
|| !item
)
3437 // Free the previous search reference and attribute list.
3439 CFRelease(params
->search
);
3440 params
->search
= NULL
;
3441 _FreeAttrList(params
->attrList
);
3442 params
->attrList
= NULL
;
3444 // Make a copy of the query dictionary so we can set the key class parameter.
3445 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableCopy(NULL
, 0, params
->query
);
3446 CFRelease(params
->query
);
3447 params
->query
= dict
;
3448 CFDictionarySetValue(dict
, kSecAttrKeyClass
, params
->assumedKeyClass
);
3450 // Determine the current item class for this search, and the next assumed key class.
3451 if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassSymmetric
)) {
3452 params
->itemClass
= kSecSymmetricKeyItemClass
;
3453 params
->assumedKeyClass
= kSecAttrKeyClassPublic
;
3454 } else if (CFEqual(params
->assumedKeyClass
, kSecAttrKeyClassPublic
)) {
3455 params
->itemClass
= kSecPublicKeyItemClass
;
3456 params
->assumedKeyClass
= kSecAttrKeyClassPrivate
;
3458 params
->itemClass
= kSecPrivateKeyItemClass
;
3459 params
->assumedKeyClass
= NULL
;
3462 // Rebuild the attribute list for the new key class.
3463 if (_CreateSecKeychainAttributeListFromDictionary(dict
, params
->itemClass
, ¶ms
->attrList
) == errSecSuccess
) {
3464 // Create a new search reference for the new attribute list.
3465 if (SecKeychainSearchCreateFromAttributes(params
->searchList
,
3467 (params
->attrList
->count
== 0) ? NULL
: params
->attrList
,
3468 (SecKeychainSearchRef
*)¶ms
->search
) == errSecSuccess
) {
3469 // Return the first matching item from the new search.
3470 // We won't come back here again until there are no more matching items for this search.
3471 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)params
->search
, (SecKeychainItemRef
*)item
);
3479 SecItemSearchCopyNext(SecItemParams
*params
, CFTypeRef
*item
)
3481 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3482 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3483 // depending on the type of search reference.
3486 CFTypeRef search
= (params
) ? params
->search
: NULL
;
3487 CFTypeID typeID
= (search
) ? CFGetTypeID(search
) : 0;
3488 if (typeID
== SecIdentitySearchGetTypeID()) {
3489 status
= SecIdentitySearchCopyNext((SecIdentitySearchRef
)search
, (SecIdentityRef
*)item
);
3491 else if (typeID
== SecKeychainSearchGetTypeID()) {
3492 status
= SecKeychainSearchCopyNext((SecKeychainSearchRef
)search
, (SecKeychainItemRef
*)item
);
3493 // Check if we need to refresh the search for the next key class
3494 while (status
== errSecItemNotFound
&& params
->assumedKeyClass
!= NULL
)
3495 status
= UpdateKeychainSearchAndCopyNext(params
, item
);
3497 else if (typeID
== 0 && (params
->useItems
|| params
->itemList
)) {
3498 // No search available, but there is an item list available.
3499 // Return the next candidate item from the caller's item list
3500 CFArrayRef itemList
= (params
->useItems
) ? params
->useItems
: params
->itemList
;
3501 CFIndex count
= CFArrayGetCount(itemList
);
3502 *item
= (CFTypeRef
) NULL
;
3503 if (params
->itemListIndex
< count
) {
3504 *item
= (CFTypeRef
)CFArrayGetValueAtIndex(itemList
, params
->itemListIndex
++);
3506 // Potentially resolve persistent item references here, and
3507 // verify the item reference we're about to hand back is still
3508 // valid (it could have been deleted from the keychain while
3509 // our query was holding onto the itemList).
3510 *item
= CopyResolvedKeychainItem(*item
);
3511 if (*item
&& (CFGetTypeID(*item
) == SecIdentityGetTypeID())) {
3512 // Persistent reference resolved to an identity, so return that type.
3513 params
->returnIdentity
= true;
3517 status
= (*item
) ? errSecSuccess
: errSecItemNotFound
;
3520 status
= errSecItemNotFound
;
3526 FilterCandidateItem(CFTypeRef
*item
, SecItemParams
*itemParams
, SecIdentityRef
*identity
)
3528 if (!item
|| *item
== NULL
|| !itemParams
)
3529 return errSecItemNotFound
;
3532 CFStringRef commonName
= NULL
;
3533 SecIdentityRef foundIdentity
= NULL
;
3534 if (CFGetTypeID(*item
) == SecIdentityGetTypeID()) {
3535 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3536 // replace the found "item" with its associated certificate (which is the
3537 // item we actually want for purposes of getting attributes, data, or a
3538 // persistent data reference), and return the identity separately.
3539 SecCertificateRef certificate
;
3540 status
= SecIdentityCopyCertificate((SecIdentityRef
) *item
, &certificate
);
3541 if (itemParams
->returnIdentity
) {
3542 foundIdentity
= (SecIdentityRef
) *item
;
3544 *identity
= foundIdentity
;
3550 *item
= (CFTypeRef
)certificate
;
3553 CFDictionaryRef query
= itemParams
->query
;
3555 if (itemParams
->itemClass
== kSecCertificateItemClass
) {
3556 // perform string comparisons first
3557 CFStringCompareFlags flags
= _StringCompareFlagsFromQuery(query
);
3558 CFStringRef nameContains
, nameStarts
, nameEnds
, nameExact
;
3559 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectContains
, (const void **)&nameContains
))
3560 nameContains
= NULL
;
3561 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectStartsWith
, (const void **)&nameStarts
))
3563 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectEndsWith
, (const void **)&nameEnds
))
3565 if (!CFDictionaryGetValueIfPresent(query
, kSecMatchSubjectWholeString
, (const void **)&nameExact
))
3567 if (nameContains
|| nameStarts
|| nameEnds
|| nameExact
) {
3568 status
= SecCertificateCopyCommonName((SecCertificateRef
)*item
, &commonName
);
3569 if (status
|| !commonName
) goto filterOut
;
3572 CFRange range
= CFStringFind(commonName
, nameContains
, flags
);
3573 if (range
.length
< 1)
3575 // certificate item contains string; proceed to next check
3578 CFRange range
= CFStringFind(commonName
, nameStarts
, flags
);
3579 if (range
.length
< 1 || range
.location
> 1)
3581 // certificate item starts with string; proceed to next check
3584 CFRange range
= CFStringFind(commonName
, nameEnds
, flags
);
3585 if (range
.length
< 1 || range
.location
!= (CFStringGetLength(commonName
) - CFStringGetLength(nameEnds
)))
3587 // certificate item ends with string; proceed to next check
3590 CFRange range
= CFStringFind(commonName
, nameExact
, flags
);
3591 if (range
.length
< 1 || (CFStringGetLength(commonName
) != CFStringGetLength(nameExact
)))
3593 // certificate item exactly matches string; proceed to next check
3595 if (itemParams
->returnIdentity
) {
3596 // if we already found and returned the identity, we can skip this
3597 if (!foundIdentity
) {
3598 status
= SecIdentityCreateWithCertificate(itemParams
->searchList
, (SecCertificateRef
) *item
, identity
);
3599 if (status
) goto filterOut
;
3601 // certificate item is part of an identity; proceed to next check
3603 if (itemParams
->policy
) {
3604 status
= _FilterWithPolicy(itemParams
->policy
, (CFDateRef
)itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3605 if (status
) goto filterOut
;
3606 // certificate item is valid for specified policy (and optionally specified date)
3608 if (itemParams
->validOnDate
) {
3609 status
= _FilterWithDate(itemParams
->validOnDate
, (SecCertificateRef
) *item
);
3610 if (status
) goto filterOut
;
3611 // certificate item is valid for specified date
3613 if (itemParams
->trustedOnly
) {
3614 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3615 // their trust has already been validated and we can skip this part.
3616 if (!(foundIdentity
&& itemParams
->returnIdentity
&& itemParams
->policy
)) {
3617 status
= _FilterWithTrust(itemParams
->trustedOnly
, (SecCertificateRef
) *item
);
3618 if (status
) goto filterOut
;
3620 // certificate item is trusted on this system
3623 if (itemParams
->itemList
) {
3624 Boolean foundMatch
= FALSE
;
3625 CFIndex idx
, count
= CFArrayGetCount(itemParams
->itemList
);
3626 for (idx
=0; idx
<count
; idx
++) {
3627 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->itemList
, idx
);
3628 SecKeychainItemRef realItem
= NULL
;
3629 SecCertificateRef aCert
= NULL
;
3630 if (anItem
== NULL
) {
3633 if (CFDataGetTypeID() == CFGetTypeID(anItem
) &&
3634 errSecSuccess
== SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
)) {
3637 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
) &&
3638 errSecSuccess
== SecIdentityCopyCertificate((SecIdentityRef
)anItem
, &aCert
)) {
3641 if (CFEqual(anItem
, (CFTypeRef
) *item
)) {
3648 CFRelease(realItem
);
3654 if (!foundMatch
) goto filterOut
;
3655 // item was found on provided list
3658 if (foundIdentity
&& !identity
) {
3659 CFRelease(foundIdentity
);
3662 CFRelease(commonName
);
3665 // if we get here, consider the item a match
3666 return errSecSuccess
;
3670 CFRelease(commonName
);
3674 if (foundIdentity
) {
3675 CFRelease(foundIdentity
);
3680 return errSecItemNotFound
;
3684 AddItemResults(SecKeychainItemRef item
,
3685 SecIdentityRef identity
,
3686 SecItemParams
*itemParams
,
3687 CFAllocatorRef allocator
,
3688 CFMutableArrayRef
*items
,
3691 // Given a found item (which may also be an identity), this function adds
3692 // the requested result types (specified in itemParams) to the appropriate
3693 // container as follows:
3695 // 1. If there is only one result type (numResultTypes == 1) and only one
3696 // match requested (maxMatches == 1), set *result directly.
3698 // 2. If there are multiple result types (numResultTypes > 1), and only one
3699 // match requested (maxMatches == 1), add each result type to itemDict
3700 // and set itemDict as the value of *result.
3702 // 3. If there is only one result type (numResultTypes == 1) and multiple
3703 // possible matches (maxMatches > 1), add the result type to *items
3704 // and set *items as the value of *result.
3706 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3707 // possible matches (maxMatches > 1), add each result type to itemDict,
3708 // add itemDict to *items, and set *items as the value of *result.
3710 // Note that we allocate *items if needed.
3712 if (!item
|| !itemParams
|| !result
)
3715 if (itemParams
->maxMatches
> 1) {
3716 // if we can return more than one item, we must have an array
3719 else if (*items
== NULL
)
3720 *items
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
3723 OSStatus tmpStatus
, status
= errSecSuccess
;
3724 CFMutableArrayRef itemArray
= (items
) ? *items
: NULL
;
3725 CFMutableDictionaryRef itemDict
= NULL
;
3726 if (itemParams
->numResultTypes
> 1) {
3727 // if we're returning more than one result type, each item we return must be a dictionary
3728 itemDict
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3731 if (itemParams
->returningRef
) {
3732 const void* itemRef
= (identity
) ? (const void*)identity
: (const void*)item
;
3734 CFDictionaryAddValue(itemDict
, kSecValueRef
, itemRef
);
3736 else if (itemArray
) {
3737 CFArrayAppendValue(itemArray
, itemRef
);
3740 *result
= CFRetain((CFTypeRef
)itemRef
);
3744 if (itemParams
->returningPersistentRef
) {
3745 CFDataRef persistentRef
;
3746 SecKeychainItemRef tmpItem
= item
;
3747 if (itemParams
->identityRef
) {
3748 tmpItem
= (SecKeychainItemRef
)itemParams
->identityRef
;
3750 tmpStatus
= SecKeychainItemCreatePersistentReference(tmpItem
, &persistentRef
);
3751 if (tmpStatus
== errSecSuccess
) {
3753 CFDictionaryAddValue(itemDict
, kSecValuePersistentRef
, persistentRef
);
3755 else if (itemArray
) {
3756 CFArrayAppendValue(itemArray
, persistentRef
);
3759 *result
= CFRetain(persistentRef
);
3761 CFRelease(persistentRef
);
3763 else if (status
== errSecSuccess
) {
3768 if (itemParams
->returningData
) {
3769 // Use SecCertificateCopyData if we have a SecCertificateRef item.
3770 // Note that a SecCertificateRef may not actually be a SecKeychainItem,
3771 // in which case SecKeychainItemCopyContent will not obtain its data.
3773 if (CFGetTypeID(item
) == SecCertificateGetTypeID()) {
3774 CFDataRef dataRef
= SecCertificateCopyData((SecCertificateRef
)item
);
3777 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3779 else if (itemArray
) {
3780 CFArrayAppendValue(itemArray
, dataRef
);
3783 *result
= CFRetain(dataRef
);
3786 status
= errSecSuccess
;
3789 status
= errSecAllocate
;
3795 tmpStatus
= SecKeychainItemCopyContent(item
, NULL
, NULL
, &length
, &data
);
3796 if (tmpStatus
== errSecSuccess
) {
3797 CFDataRef dataRef
= CFDataCreate(allocator
, (UInt8
*)data
, length
);
3799 CFDictionaryAddValue(itemDict
, kSecValueData
, dataRef
);
3801 else if (itemArray
) {
3802 CFArrayAppendValue(itemArray
, dataRef
);
3805 *result
= CFRetain(dataRef
);
3808 (void) SecKeychainItemFreeContent(NULL
, data
);
3810 else if (status
== errSecSuccess
) {
3816 if (itemParams
->returningAttributes
) {
3817 CFDictionaryRef attrsDict
= NULL
;
3818 SecItemClass itemClass
;
3819 // since we have an item, allow its actual class to override the query-specified item class
3820 tmpStatus
= SecKeychainItemCopyAttributesAndData(item
, NULL
, &itemClass
, NULL
, NULL
, NULL
);
3822 itemClass
= itemParams
->itemClass
;
3824 tmpStatus
= _CreateAttributesDictionaryFromItem(allocator
, itemClass
, item
, &attrsDict
);
3827 // add all keys and values from attrsDict to the item dictionary
3828 CFDictionaryApplyFunction(attrsDict
, _AddDictValueToOtherDict
, &itemDict
);
3830 else if (itemArray
) {
3831 CFArrayAppendValue(itemArray
, attrsDict
);
3834 *result
= CFRetain(attrsDict
);
3836 CFRelease(attrsDict
);
3838 if (tmpStatus
&& (status
== errSecSuccess
)) {
3845 CFArrayAppendValue(itemArray
, itemDict
);
3846 CFRelease(itemDict
);
3847 *result
= itemArray
;
3853 else if (itemArray
) {
3854 *result
= itemArray
;
3860 CFDataRef
_SecItemGetPersistentReference(CFTypeRef raw_item
)
3863 Item item
= ItemImpl::required((SecKeychainItemRef
)raw_item
);
3864 return item
->getPersistentRef();
3870 /******************************************************************************/
3871 #pragma mark SecItem API functions
3872 /******************************************************************************/
3875 // Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3877 static SInt32
readNumber(CFTypeRef obj
) {
3878 CFTypeID tid
= CFGetTypeID(obj
);
3880 if (tid
== CFNumberGetTypeID()) {
3881 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt32Type
, &v
);
3883 } else if (tid
== CFBooleanGetTypeID()) {
3884 v
= CFBooleanGetValue((CFBooleanRef
)obj
);
3886 } else if (tid
== CFStringGetTypeID()) {
3887 v
= CFStringGetIntValue((CFStringRef
)obj
);
3888 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v
);
3889 /* If a string converted to an int isn't equal to the int printed as
3890 a string, return a CFStringRef instead. */
3891 if (!CFEqual(t
, obj
)) {
3902 // Function to ensure the syncable keychain is unlocked.
3903 // Currently, this means unlocking the login keychain,
3904 // which will also unlock the keybag as a side effect.
3906 static OSStatus
SecItemUnlockSynchronizableKeychain()
3908 SecKeychainRef keychain
= NULL
;
3909 OSStatus status
= SecKeychainCopyLogin(&keychain
);
3911 status
= SecKeychainUnlock(keychain
, 0, NULL
, false);
3913 CFReleaseSafe(keychain
);
3918 // Function to check whether the kSecAttrSynchronizable flag is set in the query.
3920 static Boolean
SecItemSynchronizable(CFDictionaryRef query
)
3922 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
);
3923 Boolean result
= (value
&& readNumber(value
));
3929 // Function to check whether the kSecAttrNoLegacy flag is set in the query.
3931 static Boolean
SecItemNoLegacy(CFDictionaryRef query
)
3933 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrNoLegacy
);
3934 Boolean result
= (value
&& readNumber(value
));
3940 // Function to check whether the kSecAttrSynchronizable flag is set in the query,
3941 // and has the special value of kSecAttrSynchronizableAny.
3943 static Boolean
SecItemSynchronizableAny(CFDictionaryRef query
)
3945 CFTypeRef value
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
);
3947 return (CFGetTypeID(value
) == CFStringGetTypeID() &&
3948 CFEqual(value
, kSecAttrSynchronizableAny
));
3954 // Function to check whether the kSecAttrSynchronizable attribute is being updated.
3956 static Boolean
SecItemHasSynchronizableUpdate(Boolean synchronizable
, CFDictionaryRef changes
)
3958 CFTypeRef newValue
= CFDictionaryGetValue(changes
, kSecAttrSynchronizable
);
3962 Boolean new_sync
= readNumber(newValue
);
3963 Boolean old_sync
= synchronizable
;
3965 return (old_sync
!= new_sync
);
3969 // Returns true if keychain syncing is globally enabled.
3971 static Boolean
SecItemSyncEnabled()
3973 static dispatch_once_t onceToken
;
3974 static Boolean syncEnabled
= true;
3976 //sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES
3977 dispatch_once(&onceToken
, ^{
3978 CFTypeRef sync
= (CFNumberRef
)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
3980 if (sync
&& CFGetTypeID(sync
) == CFBooleanGetTypeID()) {
3981 syncEnabled
= CFBooleanGetValue((CFBooleanRef
)sync
);
3990 // Function to check whether a synchronizable persistent reference was provided.
3992 static Boolean
SecItemHasSynchronizablePersistentReference(CFDictionaryRef query
)
3994 CFTypeRef value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
3996 /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
3997 const CFIndex kSynchronizablePersistentRefLength
= sizeof(int64_t) + 4;
3998 return (CFGetTypeID(value
) == CFDataGetTypeID() &&
3999 CFDataGetLength((CFDataRef
)value
) == kSynchronizablePersistentRefLength
);
4005 // Function to apply changes to a mutable dictionary.
4006 // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
4008 static void SecItemApplyChanges(const void *key
, const void *value
, void *context
)
4010 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
) context
;
4013 CFDictionarySetValue(dict
, key
, value
);
4017 // Function to change matching items from non-syncable to syncable
4018 // (if toSyncable is true), otherwise from syncable to non-syncable.
4019 // This currently moves items between keychain containers.
4021 static OSStatus
SecItemChangeSynchronizability(CFDictionaryRef query
, CFDictionaryRef changes
, Boolean toSyncable
)
4023 // Note: the input query dictionary is a mutable copy of the query originally
4024 // provided by the caller as the first parameter to SecItemUpdate. It may not
4025 // specify returning attributes or data, but we will need both to make a copy.
4027 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnRef
);
4028 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnPersistentRef
);
4029 CFDictionaryRemoveValue((CFMutableDictionaryRef
)query
, kSecReturnData
);
4030 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnAttributes
, kCFBooleanTrue
);
4031 if (NULL
== CFDictionaryGetValue(changes
, kSecValueData
))
4032 CFDictionarySetValue((CFMutableDictionaryRef
)query
, kSecReturnData
, kCFBooleanTrue
);
4037 status
= SecItemCopyMatching_osx(query
, &result
);
4039 status
= SecItemCopyMatching_ios(query
, &result
);
4044 return errSecItemNotFound
;
4046 CFMutableArrayRef items
;
4047 if (CFGetTypeID(result
) != CFArrayGetTypeID()) {
4048 items
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4049 CFArrayAppendValue(items
, result
);
4053 items
= (CFMutableArrayRef
)result
;
4056 CFIndex idx
, count
= (items
) ? CFArrayGetCount(items
) : 0;
4057 int priority
= LOG_DEBUG
;
4059 for (idx
= 0; idx
< count
; idx
++) {
4060 CFDictionaryRef dict
= (CFDictionaryRef
) CFArrayGetValueAtIndex(items
, idx
);
4061 CFMutableDictionaryRef item
= (CFMutableDictionaryRef
)
4062 SecItemCopyTranslatedAttributes(dict
,
4063 CFDictionaryGetValue(query
, kSecClass
),
4064 (toSyncable
) ? true : false /*iOSOut*/,
4065 true /*pruneMatch*/,
4067 true /*pruneReturn*/,
4068 false /*pruneData*/,
4069 (toSyncable
) ? true : false /*pruneAccess*/);
4070 // hold onto the query before applying changes, in case the item already exists.
4071 // note that we cannot include the creation or modification dates from our
4072 // found item in this query, as they may not match the item in the other keychain.
4073 CFMutableDictionaryRef itemQuery
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
4074 CFDictionaryRemoveValue(itemQuery
, kSecAttrCreationDate
);
4075 CFDictionaryRemoveValue(itemQuery
, kSecAttrModificationDate
);
4076 // apply changes to the item dictionary that we will pass to SecItemAdd
4077 CFDictionaryApplyFunction(changes
, SecItemApplyChanges
, item
);
4079 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4080 status
= SecItemAdd_ios(item
, NULL
);
4081 secitemlog(priority
, "ChangeSync: SecItemAdd_ios=%d", status
);
4082 if (errSecDuplicateItem
== status
) {
4083 // find and apply changes to the existing syncable item.
4084 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4085 status
= SecItemUpdate_ios(itemQuery
, changes
);
4086 secitemlog(priority
, "ChangeSync: SecItemUpdate_ios=%d", status
);
4088 if (errSecSuccess
== status
) {
4089 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4090 status
= SecItemDelete_osx(itemQuery
);
4091 secitemlog(priority
, "ChangeSync: SecItemDelete_osx=%d", status
);
4095 CFDictionarySetValue(item
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4096 status
= SecItemAdd_osx(item
, NULL
);
4097 secitemlog(priority
, "ChangeSync: SecItemAdd_osx=%d", status
);
4098 if (errSecDuplicateItem
== status
) {
4099 // find and apply changes to the existing non-syncable item.
4100 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanFalse
);
4101 status
= SecItemUpdate_osx(itemQuery
, changes
);
4102 secitemlog(priority
, "ChangeSync: SecItemUpdate_osx=%d", status
);
4104 if (errSecSuccess
== status
) {
4105 CFDictionarySetValue(itemQuery
, kSecAttrSynchronizable
, kCFBooleanTrue
);
4106 status
= SecItemDelete_ios(itemQuery
);
4107 secitemlog(priority
, "ChangeSync: SecItemDelete_ios=%d", status
);
4110 CFReleaseSafe(item
);
4111 CFReleaseSafe(itemQuery
);
4115 CFReleaseSafe(items
);
4124 SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
4125 CFTypeRef ref
= NULL
;
4126 CFStringRef key_class_string
= (CFStringRef
)CFDictionaryGetValue(refAttributes
, kSecClass
);
4127 SecItemClass key_class
;
4128 bool key_class_found
= false;
4130 if (CFEqual(key_class_string
, kSecClassGenericPassword
)) {
4131 key_class
= kSecGenericPasswordItemClass
;
4132 key_class_found
= true;
4134 if (CFEqual(key_class_string
, kSecClassInternetPassword
)) {
4135 key_class
= kSecInternetPasswordItemClass
;
4136 key_class_found
= true;
4139 if (key_class_found
) {
4140 // we carry v_Data around here so the *_ios calls can find it and locate
4141 // their own data. Putting things in the attribute list doesn't help as
4142 // the osx keychainitem and item calls bail when they don't see a keychain
4143 // object. If we need to make them work we either have to bridge them, or
4144 // find a way to craft a workable keychain object. #if'ed code left below
4145 // in case we need to go down that path.
4147 struct SecKeychainAttributeList
*attrs
= (struct SecKeychainAttributeList
*)malloc(sizeof(struct SecKeychainAttributeList
) + sizeof(struct SecKeychainAttribute
) * 0);
4148 attrs
->attr
= (struct SecKeychainAttribute
*)(attrs
+ 1);
4152 // The C++ string objects need to last at least as long as the attr struct.
4155 v
= CFDictionaryGetValue(refAttributes
, CFSTR("mdat"));
4157 attrs
->attr
[attrs
->count
].tag
= kSecModDateItemAttr
;
4158 // XXX need to convert to YYYYMMDDhhmmSSZ
4159 attrs
->attr
[attrs
->count
].data
= (void*)"19690223140232Z";
4160 attrs
->attr
[attrs
->count
].length
= strlen((char*)(attrs
->attr
[attrs
->count
].data
));
4163 v
= CFDictionaryGetValue(refAttributes
, CFSTR("cdat"));
4165 attrs
->attr
[attrs
->count
].tag
= kSecCreationDateItemAttr
;
4166 // XXX need to convert to YYYYMMDDhhmmSSZ
4167 attrs
->attr
[attrs
->count
].data
= (void*)"19690223140232Z";
4168 attrs
->attr
[attrs
->count
].length
= strlen((char*)(attrs
->attr
[attrs
->count
].data
));
4172 v
= CFDictionaryGetValue(refAttributes
, CFSTR("acct"));
4174 attrs
->attr
[attrs
->count
].tag
= kSecAccountItemAttr
;
4175 account
= cfString((CFStringRef
)v
);
4176 attrs
->attr
[attrs
->count
].data
= (void*)(account
.c_str());
4177 attrs
->attr
[attrs
->count
].length
= account
.length();
4181 // class isn't treated as an attribute by the creation API
4183 v
= CFDictionaryGetValue(refAttributes
, CFSTR("svce"));
4185 attrs
->attr
[attrs
->count
].tag
= kSecServiceItemAttr
;
4186 account
= cfString((CFStringRef
)v
);
4187 attrs
->attr
[attrs
->count
].data
= (void*)(account
.c_str());
4188 attrs
->attr
[attrs
->count
].length
= account
.length();
4192 v
= CFDictionaryGetValue(refAttributes
, CFSTR("acct"));
4194 attrs
->attr
[attrs
->count
].tag
= kSecLabelItemAttr
;
4195 account
= cfString((CFStringRef
)v
);
4196 attrs
->attr
[attrs
->count
].data
= (void*)(account
.c_str());
4197 attrs
->attr
[attrs
->count
].length
= account
.length();
4201 Item item
= Item(key_class
, attrs
, 0, "");
4202 ItemImpl
*real_item
= item
.get();
4203 v
= CFDictionaryGetValue(refAttributes
, kSecValuePersistentRef
);
4205 real_item
->setPersistentRef((CFDataRef
)v
);
4207 ref
= real_item
->handle();
4209 // keys, certs, identities are not currently sync'able.
4216 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4217 * is a member of the specified application group, and is signed by Apple.
4220 SecItemValidateAppleApplicationGroupAccess(CFStringRef group
)
4222 SecTrustedApplicationRef app
= NULL
;
4223 SecRequirementRef requirement
= NULL
;
4224 SecCodeRef code
= NULL
;
4225 OSStatus status
= errSecParam
;
4228 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(group
), kCFStringEncodingUTF8
) + 1;
4229 char* buffer
= (char*) malloc(length
);
4231 if (CFStringGetCString(group
, buffer
, length
, kCFStringEncodingUTF8
)) {
4232 status
= SecTrustedApplicationCreateApplicationGroup(buffer
, NULL
, &app
);
4236 status
= errSecMemoryError
;
4240 status
= SecTrustedApplicationCopyRequirement(app
, &requirement
);
4243 status
= SecCodeCopySelf(kSecCSDefaultFlags
, &code
);
4246 status
= SecCodeCheckValidity(code
, kSecCSDefaultFlags
, requirement
);
4249 CFReleaseSafe(code
);
4250 CFReleaseSafe(requirement
);
4256 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4257 * and attempts to return a sanitized copy for passing to the underlying
4258 * platform-specific implementation code.
4260 * If iOSOut is true, one or more translations may apply:
4261 * - SecKeychain refs are removed, since there aren't multiple keychains
4262 * - SecPolicy refs are removed, since they can't be externalized
4263 * - SecAccess refs are removed, and potentially translated to entitlements
4265 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4266 * parameter errors due to strict input checks in secd, which only permits
4267 * these constants for calls to SecItemCopyMatching.
4269 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4270 * This permits a query to be reused for non-synchronizable items, or to
4271 * resolve a search based on a persistent item reference for iOS.
4273 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4274 * parameter errors due to strict input checks in secd, which do not permit
4275 * these constants for calls to SecItemUpdate.
4278 SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict
, CFTypeRef itemClass
,
4279 bool iOSOut
, bool pruneMatch
, bool pruneSync
, bool pruneReturn
, bool pruneData
, bool pruneAccess
)
4281 CFMutableDictionaryRef result
= CFDictionaryCreateMutableCopy(NULL
, 0, inOSXDict
);
4282 if (result
== NULL
) {
4287 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4291 /* Match constants are only supported on iOS for SecItemCopyMatching,
4292 * and will generate an error if passed to other SecItem API functions;
4293 * on OS X, they're just ignored if not applicable for the context.
4295 CFDictionaryRemoveValue(result
, kSecMatchPolicy
);
4296 CFDictionaryRemoveValue(result
, kSecMatchItemList
);
4297 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4298 CFDictionaryRemoveValue(result
, kSecMatchIssuers
);
4299 CFDictionaryRemoveValue(result
, kSecMatchEmailAddressIfPresent
);
4300 CFDictionaryRemoveValue(result
, kSecMatchSubjectContains
);
4301 CFDictionaryRemoveValue(result
, kSecMatchCaseInsensitive
);
4302 CFDictionaryRemoveValue(result
, kSecMatchTrustedOnly
);
4303 CFDictionaryRemoveValue(result
, kSecMatchValidOnDate
);
4304 CFDictionaryRemoveValue(result
, kSecMatchLimit
);
4305 CFDictionaryRemoveValue(result
, kSecMatchLimitOne
);
4306 CFDictionaryRemoveValue(result
, kSecMatchLimitAll
);
4310 /* Return constants are not supported on iOS for SecItemUpdate,
4311 * where they will generate an error; on OS X, they're just ignored
4312 * if not applicable for the context.
4314 CFDictionaryRemoveValue(result
, kSecReturnData
);
4315 CFDictionaryRemoveValue(result
, kSecReturnAttributes
);
4316 CFDictionaryRemoveValue(result
, kSecReturnRef
);
4317 CFDictionaryRemoveValue(result
, kSecReturnPersistentRef
);
4321 /* Searching on data is not supported. */
4322 CFDictionaryRemoveValue(result
, kSecValueData
);
4326 /* Searching on access lists is not supported */
4327 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4331 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4332 * cannot specify a keychain search list on iOS
4334 CFDictionaryRemoveValue(result
, kSecMatchSearchList
);
4336 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4337 * cannot specify a keychain on iOS
4339 CFDictionaryRemoveValue(result
, kSecUseKeychain
);
4341 /* Remove kSecMatchPolicy (value is a SecPolicyRef);
4342 * TODO: need a way to externalize and restore a policy instance
4344 CFDictionaryRemoveValue(result
, kSecMatchPolicy
);
4346 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4347 * unless kSecAttrAccessGroup has already been specified.
4349 SecAccessRef access
= (SecAccessRef
) CFDictionaryGetValue(result
, kSecAttrAccess
);
4350 CFStringRef accessGroup
= (CFStringRef
) CFDictionaryGetValue(result
, kSecAttrAccessGroup
);
4351 if (access
!= NULL
&& accessGroup
== NULL
) {
4352 /* Translate "InternetAccounts" application group to an access group */
4353 if (errSecSuccess
== SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4354 /* The caller is a valid member of the application group. */
4355 CFStringRef groupName
= CFSTR("appleaccount");
4356 CFTypeRef value
= CFDictionaryGetValue(result
, kSecAttrAuthenticationType
);
4357 if (value
&& CFEqual(value
, kSecAttrAuthenticationTypeHTMLForm
)) {
4358 groupName
= CFSTR("com.apple.cfnetwork");
4360 CFDictionarySetValue(result
, kSecAttrAccessGroup
, groupName
);
4363 CFDictionaryRemoveValue(result
, kSecAttrAccess
);
4365 /* If item is specified by direct reference, and this is an iOS search,
4366 * replace it with a persistent reference.
4368 CFTypeRef directRef
= CFDictionaryGetValue(result
, kSecValueRef
);
4370 CFDataRef persistentRef
= _SecItemGetPersistentReference(directRef
);
4371 if (persistentRef
) {
4372 CFDictionarySetValue(result
, kSecValuePersistentRef
, persistentRef
);
4374 CFDictionaryRemoveValue(result
, kSecValueRef
);
4377 /* If item is specified by persistent reference, and this is an iOS search,
4378 * remove the synchronizable attribute as it will be rejected by secd.
4380 CFTypeRef persistentRef
= CFDictionaryGetValue(result
, kSecValuePersistentRef
);
4381 if (persistentRef
) {
4382 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4385 /* Remove kSecAttrModificationDate; this should never be used as criteria
4386 * for a search, or to add/modify an item. (If we are cloning an item
4387 * and want to keep its modification date, we don't call this function.)
4388 * It turns out that some clients are using the full attributes dictionary
4389 * returned by SecItemCopyMatching as a query to find the same item later,
4390 * which won't work once the item is updated.
4392 CFDictionaryRemoveValue(result
, kSecAttrModificationDate
);
4395 /* iOS doesn't add the class attribute, so we must do it here. */
4397 CFDictionarySetValue(result
, kSecClass
, itemClass
);
4399 /* Remove attributes which are not part of the OS X database schema. */
4400 CFDictionaryRemoveValue(result
, kSecAttrAccessible
);
4401 CFDictionaryRemoveValue(result
, kSecAttrAccessGroup
);
4402 CFDictionaryRemoveValue(result
, kSecAttrSynchronizable
);
4403 CFDictionaryRemoveValue(result
, kSecAttrTombstone
);
4406 /* This attribute is consumed by the bridge itself. */
4407 CFDictionaryRemoveValue(result
, kSecAttrNoLegacy
);
4413 * SecItemCopyMergedResults takes two input objects, which may be containers,
4414 * and returns a retained object which merges the results. Merging depends on the
4415 * result type. If each result is valid and is not an array, then only one match was
4416 * requested; in that case, the syncable (ios) match is preferred.
4418 * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a
4419 * particular keychain to search, we do not want to merge in any IOS results. Also, may need
4420 * to filter out duplicates if two items differ only in the sync attribute.
4423 SecItemCopyMergedResults(CFDictionaryRef query
, CFTypeRef result_osx
, CFTypeRef result_ios
)
4425 CFTypeID id_osx
= (result_osx
) ? CFGetTypeID(result_osx
) : 0;
4426 CFTypeID id_ios
= (result_ios
) ? CFGetTypeID(result_ios
) : 0;
4427 CFTypeID id_array
= CFArrayGetTypeID();
4428 if ((id_osx
== id_array
) && (id_ios
== id_array
)) {
4429 // Fold the arrays into one.
4430 CFMutableArrayRef results
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4431 CFArrayAppendArray(results
, (CFArrayRef
)result_ios
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_ios
)));
4432 CFArrayAppendArray(results
, (CFArrayRef
)result_osx
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
)result_osx
)));
4435 // Result type is not an array, so only one match can be returned.
4436 return (id_ios
) ? CFRetain(result_ios
) : CFRetain(result_osx
);
4443 SecItemCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
)
4445 secitemlog(LOG_NOTICE
, "SecItemCopyMatching");
4449 secitemshow(query
, "SecItemCopyMatching query:");
4451 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4452 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4453 Boolean ios_only
= SecItemNoLegacy(query
);
4454 Boolean sync_enabled
= SecItemSyncEnabled();
4455 Boolean search_ios
= SecItemSynchronizable(query
);
4456 Boolean merge_search
= SecItemSynchronizableAny(query
);
4457 Boolean persistref_ios
= SecItemHasSynchronizablePersistentReference(query
);
4459 if (ios_only
|| (sync_enabled
&& (merge_search
|| persistref_ios
|| search_ios
))) {
4460 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4461 CFDictionaryGetValue(query
, kSecClass
), true, false, false, false, true, true);
4463 status_ios
= errSecParam
;
4466 SecItemUnlockSynchronizableKeychain();
4467 status_ios
= SecItemCopyMatching_ios(attrs_ios
, &result_ios
);
4468 CFRelease(attrs_ios
);
4470 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_ios result: %d", status_ios
);
4471 if (ios_only
|| !merge_search
|| persistref_ios
) {
4472 AssignOrReleaseResult(result_ios
, result
);
4473 return status_ios
; // no need to search non-syncable keychains
4477 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4478 CFDictionaryGetValue(query
, kSecClass
), false, false, true, false, true, true);
4480 status_osx
= errSecParam
;
4483 status_osx
= SecItemCopyMatching_osx(attrs_osx
, &result_osx
);
4484 CFRelease(attrs_osx
);
4486 secitemlog(LOG_NOTICE
, "SecItemCopyMatching_osx result: %d", status_osx
);
4488 // If one of the searches failed to occur or produce results, we can eliminate it
4489 if (result_ios
== NULL
) {
4490 AssignOrReleaseResult(result_osx
, result
);
4491 return status_osx
; // we can only have non-syncable results
4493 if (result_osx
== NULL
) {
4494 AssignOrReleaseResult(result_ios
, result
);
4495 return status_ios
; // we can only have syncable results
4498 // If we get here, need to merge results
4499 CFTypeRef result_merged
= SecItemCopyMergedResults(query
, result_osx
, result_ios
);
4500 CFReleaseSafe(result_osx
);
4501 CFReleaseSafe(result_ios
);
4502 AssignOrReleaseResult(result_merged
, result
);
4504 if (status_osx
== status_ios
) {
4505 return status_osx
; // both searches produced the same result
4507 else if (!status_osx
|| !status_ios
) {
4508 return errSecSuccess
; // one of the searches succeeded
4510 else if (status_osx
== errSecItemNotFound
) {
4511 return status_ios
; // this failure was more interesting
4517 SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
)
4519 secitemlog(LOG_NOTICE
, "SecItemAdd");
4526 secitemshow(attributes
, "SecItemAdd attrs:");
4528 OSStatus status_osx
, status_ios
;
4529 CFTypeRef result_osx
= NULL
, result_ios
= NULL
;
4530 Boolean ios_only
= SecItemNoLegacy(attributes
);
4531 Boolean sync_enabled
= SecItemSyncEnabled();
4532 Boolean add_ios
= SecItemSynchronizable(attributes
);
4534 if (ios_only
|| (sync_enabled
&& add_ios
)) {
4535 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(attributes
,
4536 NULL
, true, true, false, false, false, false);
4538 status_ios
= errSecParam
;
4541 SecItemUnlockSynchronizableKeychain();
4542 status_ios
= SecItemAdd_ios(attrs_ios
, &result_ios
);
4543 CFRelease(attrs_ios
);
4545 secitemlog(LOG_NOTICE
, "SecItemAdd_ios result: %d", status_ios
);
4547 *result
= result_ios
;
4549 CFReleaseSafe(result_ios
);
4553 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(attributes
,
4554 NULL
, false, false, true, false, false, false);
4556 status_osx
= errSecParam
;
4559 status_osx
= SecItemAdd_osx(attrs_osx
, &result_osx
);
4560 CFRelease(attrs_osx
);
4562 secitemlog(LOG_NOTICE
, "SecItemAdd_osx result: %d", status_osx
);
4564 *result
= result_osx
;
4566 CFReleaseSafe(result_osx
);
4571 SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
)
4573 secitemlog(LOG_NOTICE
, "SecItemUpdate");
4574 if (!query
|| !attributesToUpdate
) {
4577 secitemshow(query
, "SecItemUpdate query:");
4578 secitemshow(attributesToUpdate
, "SecItemUpdate attrs:");
4580 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4581 Boolean ios_only
= SecItemNoLegacy(query
);
4582 Boolean sync_enabled
= SecItemSyncEnabled();
4583 Boolean search_ios
= SecItemSynchronizable(query
);
4584 Boolean merge_search
= SecItemSynchronizableAny(query
);
4585 Boolean persistref_ios
= SecItemHasSynchronizablePersistentReference(query
);
4587 if (ios_only
|| (sync_enabled
&& (merge_search
|| persistref_ios
|| search_ios
))) {
4588 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4589 CFDictionaryGetValue(query
, kSecClass
), true, true, false, true, true, true);
4591 status_ios
= errSecParam
;
4594 SecItemUnlockSynchronizableKeychain();
4595 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate
))
4596 status_ios
= SecItemChangeSynchronizability(attrs_ios
, attributesToUpdate
, false);
4598 status_ios
= SecItemUpdate_ios(attrs_ios
, attributesToUpdate
);
4599 CFRelease(attrs_ios
);
4601 secitemlog(LOG_NOTICE
, "SecItemUpdate_ios result: %d", status_ios
);
4602 if (ios_only
|| !merge_search
|| persistref_ios
)
4606 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4607 CFDictionaryGetValue(query
, kSecClass
), false, false, true, true, true, true);
4609 status_osx
= errSecParam
;
4612 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate
))
4613 status_osx
= SecItemChangeSynchronizability(attrs_osx
, attributesToUpdate
, true);
4615 status_osx
= SecItemUpdate_osx(attrs_osx
, attributesToUpdate
);
4617 CFRelease(attrs_osx
);
4619 secitemlog(LOG_NOTICE
, "SecItemUpdate_osx result: %d", status_osx
);
4621 // Harmonize the result of the update attempts.
4622 if (status_osx
== status_ios
) {
4623 // both updates produced the same result
4626 else if (!status_osx
|| !status_ios
) {
4627 // one of the updates succeeded, but the other failed
4628 if (status_osx
== errSecItemNotFound
|| status_ios
== errSecItemNotFound
)
4629 return errSecSuccess
; // item only found in one keychain
4631 return (status_osx
) ? status_osx
: status_ios
; // return the error
4633 else if (status_osx
== errSecItemNotFound
) {
4634 // both updates failed, status_ios failure is more interesting
4635 // since the item was actually found
4643 SecItemDelete(CFDictionaryRef query
)
4645 secitemlog(LOG_NOTICE
, "SecItemDelete");
4649 secitemshow(query
, "SecItemDelete query:");
4651 OSStatus status_osx
= errSecItemNotFound
, status_ios
= errSecItemNotFound
;
4652 Boolean ios_only
= SecItemNoLegacy(query
);
4653 Boolean sync_enabled
= SecItemSyncEnabled();
4654 Boolean search_ios
= SecItemSynchronizable(query
);
4655 Boolean merge_search
= SecItemSynchronizableAny(query
);
4656 Boolean persistref_ios
= SecItemHasSynchronizablePersistentReference(query
);
4658 if (ios_only
|| (sync_enabled
&& (merge_search
|| persistref_ios
|| search_ios
))) {
4659 CFDictionaryRef attrs_ios
= SecItemCopyTranslatedAttributes(query
,
4660 NULL
, true, true, false, true, true, true);
4662 status_ios
= errSecParam
;
4665 SecItemUnlockSynchronizableKeychain();
4666 status_ios
= SecItemDelete_ios(attrs_ios
);
4667 CFRelease(attrs_ios
);
4669 secitemlog(LOG_NOTICE
, "SecItemDelete_ios result: %d", status_ios
);
4670 if (ios_only
|| !merge_search
|| persistref_ios
)
4674 CFDictionaryRef attrs_osx
= SecItemCopyTranslatedAttributes(query
,
4675 NULL
, false, false, true, true, true, true);
4677 status_osx
= errSecParam
;
4680 status_osx
= SecItemDelete_osx(attrs_osx
);
4681 CFRelease(attrs_osx
);
4683 secitemlog(LOG_NOTICE
, "SecItemDelete_osx result: %d", status_osx
);
4686 // Harmonize the result of the delete attempts.
4687 if (status_osx
== status_ios
) {
4688 // both deletes produced the same result
4691 else if (!status_osx
|| !status_ios
) {
4692 // one of the deletes succeeded, but the other failed
4693 if (status_osx
== errSecItemNotFound
|| status_ios
== errSecItemNotFound
)
4694 return errSecSuccess
; // item only found in one keychain
4696 return (status_osx
) ? status_osx
: status_ios
; // return the error
4698 else if (status_osx
== errSecItemNotFound
) {
4699 // both deletes failed, status_ios failure is more interesting
4700 // since the item was actually found
4708 SecItemCopyMatching_osx(
4709 CFDictionaryRef query
,
4712 if (!query
|| !result
)
4717 CFAllocatorRef allocator
= CFGetAllocator(query
);
4718 CFIndex matchCount
= 0;
4719 CFMutableArrayRef itemArray
= NULL
;
4720 SecKeychainItemRef item
= NULL
;
4721 SecIdentityRef identity
= NULL
;
4722 OSStatus tmpStatus
, status
= errSecSuccess
;
4724 // validate input query parameters and create the search reference
4725 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(query
, &status
);
4726 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
4728 // find the next match until we hit maxMatches, or no more matches found
4729 while ( !(!itemParams
->returnAllMatches
&& matchCount
>= itemParams
->maxMatches
) &&
4730 SecItemSearchCopyNext(itemParams
, (CFTypeRef
*)&item
) == errSecSuccess
) {
4732 if (FilterCandidateItem((CFTypeRef
*)&item
, itemParams
, &identity
))
4733 continue; // move on to next item
4735 ++matchCount
; // we have a match
4737 tmpStatus
= AddItemResults(item
, identity
, itemParams
, allocator
, &itemArray
, result
);
4738 if (tmpStatus
&& (status
== errSecSuccess
))
4746 CFRelease(identity
);
4751 if (status
== errSecSuccess
)
4752 status
= (matchCount
> 0) ? errSecSuccess
: errSecItemNotFound
;
4755 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
4759 _FreeSecItemParams(itemParams
);
4765 SecItemCopyDisplayNames(
4767 CFArrayRef
*displayNames
)
4771 Required(displayNames
);
4773 return errSecUnimplemented
;
4779 CFDictionaryRef attributes
,
4787 CFAllocatorRef allocator
= CFGetAllocator(attributes
);
4788 CFMutableArrayRef itemArray
= NULL
;
4789 SecKeychainItemRef item
= NULL
;
4790 OSStatus tmpStatus
, status
= errSecSuccess
;
4792 // validate input attribute parameters
4793 SecItemParams
*itemParams
= _CreateSecItemParamsFromDictionary(attributes
, &status
);
4794 require_action(itemParams
!= NULL
, error_exit
, itemParams
= NULL
);
4796 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
4797 // since the private key should already be in a keychain by definition. We could support
4798 // this as a copy operation for the private key if a different keychain is specified,
4799 // but in any case it should try to add the certificate. See <rdar://8317887>.
4800 require_action(!itemParams
->returnIdentity
, error_exit
, status
= errSecItemInvalidValue
);
4802 if (!itemParams
->useItems
) {
4803 // create a single keychain item specified by the input attributes
4804 status
= SecKeychainItemCreateFromContent(itemParams
->itemClass
,
4805 itemParams
->attrList
,
4806 (itemParams
->itemData
) ? (UInt32
)CFDataGetLength(itemParams
->itemData
) : 0,
4807 (itemParams
->itemData
) ? CFDataGetBytePtrVoid(itemParams
->itemData
) : NULL
,
4808 itemParams
->keychain
,
4811 require_noerr(status
, error_exit
);
4813 // return results (if requested)
4815 itemParams
->maxMatches
= 1; // in case kSecMatchLimit was set to > 1
4816 tmpStatus
= AddItemResults(item
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4817 if (tmpStatus
&& (status
== errSecSuccess
))
4823 // add multiple items which are specified in the itemParams->useItems array.
4824 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
4825 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
4826 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
4828 OSStatus aggregateStatus
= errSecSuccess
;
4829 CFIndex ix
, count
= CFArrayGetCount(itemParams
->useItems
);
4830 itemParams
->maxMatches
= (count
> 1) ? (int)count
: 2; // force results to always be returned as an array
4831 for (ix
=0; ix
< count
; ix
++) {
4832 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(itemParams
->useItems
, ix
);
4834 if (SecCertificateGetTypeID() == CFGetTypeID(anItem
)) {
4835 // SecCertificateRef item
4836 tmpStatus
= SecCertificateAddToKeychain((SecCertificateRef
)anItem
, itemParams
->keychain
);
4837 if (!tmpStatus
&& result
) {
4838 tmpStatus
= AddItemResults((SecKeychainItemRef
)anItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4840 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
4842 else if (SecKeyGetTypeID() == CFGetTypeID(anItem
)) {
4844 SecKeychainRef itemKeychain
= NULL
;
4845 tmpStatus
= SecKeychainItemCopyKeychain((SecKeychainItemRef
)anItem
, &itemKeychain
);
4846 if (tmpStatus
== errSecSuccess
) {
4847 // key was in a keychain, so we can attempt to copy it
4848 SecKeychainItemRef itemCopy
= NULL
;
4849 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
4850 if (!tmpStatus
&& result
) {
4851 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4854 CFRelease(itemCopy
);
4858 // key was not in any keychain, so must be imported
4859 SecKeychainItemRef keyItem
= NULL
;
4860 tmpStatus
= _ImportKey((SecKeyRef
)anItem
, itemParams
->keychain
, itemParams
->access
, itemParams
->attrList
, &keyItem
);
4861 if (!tmpStatus
&& result
) {
4862 tmpStatus
= AddItemResults(keyItem
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4869 CFRelease(itemKeychain
);
4871 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
4873 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem
)) {
4874 // SecKeychainItemRef item
4875 SecKeychainItemRef itemCopy
= NULL
;
4876 tmpStatus
= SecKeychainItemCreateCopy((SecKeychainItemRef
)anItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
4877 if (!tmpStatus
&& result
) {
4878 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4881 CFRelease(itemCopy
);
4883 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
4885 else if (CFDataGetTypeID() == CFGetTypeID(anItem
)) {
4886 // CFDataRef item (persistent reference)
4887 SecKeychainItemRef realItem
= NULL
;
4888 tmpStatus
= SecKeychainItemCopyFromPersistentReference((CFDataRef
)anItem
, &realItem
);
4889 if (tmpStatus
== errSecSuccess
) {
4890 // persistent reference resolved to a keychain item, so we can attempt to copy it
4891 SecKeychainItemRef itemCopy
= NULL
;
4892 tmpStatus
= SecKeychainItemCreateCopy(realItem
, itemParams
->keychain
, itemParams
->access
, &itemCopy
);
4893 if (!tmpStatus
&& result
) {
4894 tmpStatus
= AddItemResults(itemCopy
, NULL
, itemParams
, allocator
, &itemArray
, result
);
4897 CFRelease(itemCopy
);
4901 CFRelease(realItem
);
4903 aggregateStatus
= _UpdateAggregateStatus(tmpStatus
, aggregateStatus
, errSecDuplicateItem
);
4906 } // end of itemList array loop
4907 status
= aggregateStatus
;
4908 } // end processing multiple items
4911 if (status
!= errSecSuccess
&& result
!= NULL
&& *result
!= NULL
) {
4915 _FreeSecItemParams(itemParams
);
4922 CFDictionaryRef query
,
4923 CFDictionaryRef attributesToUpdate
)
4925 if (!query
|| !attributesToUpdate
)
4928 // run the provided query to get a list of items to update
4929 CFTypeRef results
= NULL
;
4930 OSStatus status
= SecItemCopyMatching(query
, &results
);
4931 if (status
!= errSecSuccess
)
4932 return status
; // nothing was matched, or the query was bad
4934 CFArrayRef items
= NULL
;
4935 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
4936 items
= (CFArrayRef
) results
;
4939 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
4943 OSStatus result
= errSecSuccess
;
4944 CFIndex ix
, count
= CFArrayGetCount(items
);
4945 for (ix
=0; ix
< count
; ix
++) {
4946 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
4948 status
= _UpdateKeychainItem(anItem
, attributesToUpdate
);
4949 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);
4961 CFDictionaryRef query
)
4966 // run the provided query to get a list of items to delete
4967 CFTypeRef results
= NULL
;
4968 OSStatus status
= SecItemCopyMatching_osx(query
, &results
);
4969 if (status
!= errSecSuccess
)
4970 return status
; // nothing was matched, or the query was bad
4972 CFArrayRef items
= NULL
;
4973 if (CFArrayGetTypeID() == CFGetTypeID(results
)) {
4974 items
= (CFArrayRef
) results
;
4977 items
= CFArrayCreate(NULL
, &results
, 1, &kCFTypeArrayCallBacks
);
4981 OSStatus result
= errSecSuccess
;
4982 CFIndex ix
, count
= CFArrayGetCount(items
);
4983 for (ix
=0; ix
< count
; ix
++) {
4984 CFTypeRef anItem
= (CFTypeRef
) CFArrayGetValueAtIndex(items
, ix
);
4986 if (SecIdentityGetTypeID() == CFGetTypeID(anItem
)) {
4987 status
= _DeleteIdentity((SecIdentityRef
)anItem
);
4990 status
= _DeleteKeychainItem(anItem
);
4992 result
= _UpdateAggregateStatus(status
, result
, errSecSuccess
);