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