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