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