]> git.saurik.com Git - apple/security.git/blame - Security/libsecurity_keychain/lib/SecItem.cpp
Security-57031.30.12.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecItem.cpp
CommitLineData
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
50const uint8_t kUUIDStringLength = 36;
b1ab9ed8
A
51
52OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result);
53OSStatus SecItemCopyMatching_osx(CFDictionaryRef query, CFTypeRef *result);
54OSStatus SecItemUpdate_osx(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
55OSStatus SecItemDelete_osx(CFDictionaryRef query);
56
57extern "C" {
58OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
59OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
60OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
61OSStatus SecItemDelete_ios(CFDictionaryRef query);
427c49bc
A
62
63CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
64CFTypeRef SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios);
65OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group);
66CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
67 bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess);
68}
69
d8f41ccd
A
70static Boolean SecItemSynchronizable(CFDictionaryRef query);
71
427c49bc
A
72static 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
87static 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
115struct ProtocolAttributeInfo {
116 const CFTypeRef *protocolValue;
117 SecProtocolType protocolType;
118};
119
120static 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
154static const int kNumberOfProtocolTypes = sizeof(gProtocolTypes) / sizeof(ProtocolAttributeInfo);
155
156/*
157 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
158 */
159static 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 */
181static 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
200struct AuthenticationAttributeInfo {
201 const CFTypeRef *authValue;
202 SecAuthenticationType authType;
203};
204
205static 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
216static const int kNumberOfAuthenticationTypes = sizeof(gAuthTypes) / sizeof(AuthenticationAttributeInfo);
217
218/*
219 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
220 * SecAttrAuthenticationType to a SecAuthenticationType.
221 */
222static 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 */
245static 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
264struct KeyAlgorithmInfo {
265 const CFTypeRef *keyType;
266 UInt32 keyValue;
267};
268
269static 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
282static const int kNumberOfKeyTypes = sizeof(gKeyTypes) / sizeof (KeyAlgorithmInfo);
283
284
285static 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
306enum ItemRepresentation
307{
308 kStringRepresentation,
309 kDataRepresentation,
310 kNumberRepresentation,
311 kBooleanRepresentation,
312 kDateRepresentation
313};
314
315
316struct InternalAttributeListInfo
317{
318 UInt32 oldItemType;
319 const CFTypeRef *newItemType;
320 ItemRepresentation itemRepresentation;
321};
322
323
324static 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
340static const int kNumberOfGenericPasswordAttributes = sizeof(gGenericPasswordAttributes) / sizeof (InternalAttributeListInfo);
341
427c49bc 342#if 0
b1ab9ed8
A
343static 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
362static const int kNumberOfInternetPasswordAttributes = sizeof(gInternetPasswordAttributes) / sizeof (InternalAttributeListInfo);
427c49bc 363#endif
b1ab9ed8
A
364
365static 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
377static const int kNumberOfCertificateAttributes = sizeof(gCertificateAttributes) / sizeof(InternalAttributeListInfo);
378
379
380static 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
411static const int kNumberOfKeyAttributes = sizeof(gKeyAttributes) / sizeof(InternalAttributeListInfo);
412
413
414static 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
518static 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
601static 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 */
784static 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 */
809static 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 */
832static 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
1059error_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 */
1080static 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
1296SecKeychainItemCopyContent_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 */
1306static 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 */
1341static 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 */
1368static 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
1391malloc_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 */
1403static 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
1426malloc_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 */
1439static 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 */
1455static 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 */
1471static 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
1712malloc_number_failed:
1713CFDataCreateAttribute_failed:
1714CFStringCreateAttribute_failed:
1715malloc_attrPtr_failed:
1716
1717 // free any attributes
1718 _FreeAttrList(attrListPtr);
1719
1720calloc_attrListPtr_failed:
1721
1722 return ( errSecBufferTooSmall );
1723
1724#endif
1725}
1726
427c49bc
A
1727static 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 */
1769static 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
1945malloc_authenticationType_failed:
1946malloc_protocol_failed:
1947malloc_port_failed:
1948CFStringCreateAttribute_failed:
1949malloc_attrPtr_failed:
1950
1951 // free any attributes
1952 _FreeAttrList(attrListPtr);
1953
1954calloc_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 */
1967static 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 */
2000static 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 */
2055static 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
2114error_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 */
2149static 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, &currentAppRef);
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
2246finish:
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
2267static 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
2387replace_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 2403static 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
2507update_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 2521static 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 2569static 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 2601static 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 2622static 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
2635static 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
2661static 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
2692static 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
2728static 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
2791struct 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
2829static 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
2863static 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
2896static 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
2909static 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
2940static 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) {
3028 itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; // no key class specified, so start with symmetric key class; will search the others later
3029 }
3030 require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
3031 }
3032
3033 itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
3034 itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3035 itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
3036
3037 // other input attributes, used for SecItemAdd but not for finding items
3038 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
3039 if (itemParams->access == NULL) {
3040 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3041 require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
3042 }
3043
b1ab9ed8
A
3044 // determine how to return the result
3045 itemParams->numResultTypes = 0;
3046 itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3047 if (itemParams->returningRef) ++itemParams->numResultTypes;
3048 itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3049 if (itemParams->returningPersistentRef) ++itemParams->numResultTypes;
3050 itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3051 if (itemParams->returningAttributes) ++itemParams->numResultTypes;
3052 itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3053 if (itemParams->returningData) ++itemParams->numResultTypes;
3054
3055 // default is kSecReturnRef if no result types were specified
3056 if (!itemParams->numResultTypes) {
3057 itemParams->returningRef = TRUE;
3058 itemParams->numResultTypes = 1;
3059 }
3060
3061 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3062 itemParams->maxMatches = 1;
3063 itemParams->returnAllMatches = FALSE;
3064 if (itemParams->matchLimit) {
3065 if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
3066 itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit);
3067 }
3068 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
3069 CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches);
3070 require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported);
3071 }
3072 }
3073 if (itemParams->returnAllMatches) {
3074 itemParams->maxMatches = INT32_MAX;
3075 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3076 if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
3077 status = errSecReturnDataUnsupported;
3078 require_noerr(status, error_exit);
3079 }
3080
427c49bc
A
3081 // 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
3082 if (itemParams->useItems) {
3083 if (itemParams->itemClass == 0) {
3084 itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
3085 }
3086 status = errSecSuccess;
3087 goto error_exit; // all done here
b1ab9ed8
A
3088 }
3089
3090 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3091 require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
3092
3093 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3094 if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
3095 // searching for certificates by email address
3096 char *nameBuf = (char*)malloc(MAXPATHLEN);
3097 if (!nameBuf) {
427c49bc 3098 status = errSecAllocate;
b1ab9ed8
A
3099 }
3100 else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) {
3101 status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search);
3102 }
3103 else {
3104 status = errSecItemInvalidValue;
3105 }
3106 if (nameBuf) free(nameBuf);
3107 }
3108 else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) {
3109 // searching for certificates by issuer and serial number
3110 status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
3111 (CFDataRef)itemParams->issuer,
3112 (CFDataRef)itemParams->serialNumber,
3113 (SecKeychainSearchRef*)&itemParams->search);
3114 }
3115 else if (itemParams->returnIdentity && itemParams->policy) {
3116 // searching for identities by policy
3117 status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
3118 (CFStringRef)itemParams->service,
3119 itemParams->keyUsage,
3120 itemParams->searchList,
3121 itemParams->trustedOnly,
3122 (SecIdentitySearchRef*)&itemParams->search);
3123 }
3124 else if (itemParams->returnIdentity) {
3125 // searching for identities
3126 status = SecIdentitySearchCreate(itemParams->searchList,
3127 itemParams->keyUsage,
3128 (SecIdentitySearchRef*)&itemParams->search);
3129 }
3130 else {
3131 // normal keychain item search
3132 status = SecKeychainSearchCreateFromAttributes(itemParams->searchList,
3133 itemParams->itemClass,
3134 (itemParams->attrList->count == 0) ? NULL : itemParams->attrList,
3135 (SecKeychainSearchRef*)&itemParams->search);
3136 }
3137
3138error_exit:
3139 if (status) {
3140 _FreeSecItemParams(itemParams);
3141 itemParams = NULL;
3142 }
3143 if (error) {
3144 *error = status;
3145 }
3146 return itemParams;
3147}
3148
3149
427c49bc 3150static OSStatus
b1ab9ed8
A
3151_ImportKey(
3152 SecKeyRef keyRef,
3153 SecKeychainRef keychainRef,
3154 SecAccessRef accessRef,
3155 SecKeychainAttributeList *attrList,
3156 SecKeychainItemRef *outItemRef)
3157{
3158 BEGIN_SECAPI
3159
3160 // We must specify the access, since a free-floating key won't have one yet by default
3161 SecPointer<Access> access;
3162 if (accessRef) {
3163 access = Access::required(accessRef);
3164 }
3165 else {
3166 CFStringRef descriptor = NULL;
3167 if (attrList) {
3168 for (UInt32 index=0; index < attrList->count; index++) {
3169 SecKeychainAttribute attr = attrList->attr[index];
3170 if (attr.tag == kSecKeyPrintName) {
3171 descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE);
3172 break;
3173 }
3174 }
3175 }
3176 if (descriptor == NULL) {
3177 descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>"));
3178 }
3179 access = new Access(cfString(descriptor));
3180 CFRelease(descriptor);
3181 }
3182
3183 KeyItem *key = KeyItem::required(keyRef);
3184 Item item = key->importTo(Keychain::optional(keychainRef), access, attrList);
3185 if (outItemRef)
3186 *outItemRef = item->handle();
3187
3188 END_SECAPI
3189}
3190
427c49bc 3191static Boolean
b1ab9ed8
A
3192_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence)
3193{
3194 /* Check for ignorable status codes in leaf certificate's evidence */
3195 Boolean result = true;
3196 unsigned int i;
3197 for (i=0; i < evidence->NumStatusCodes; i++) {
3198 CSSM_RETURN scode = evidence->StatusCodes[i];
3199 if (scode == CSSMERR_APPLETP_INVALID_CA) {
3200 // the TP has rejected this CA cert because it's in the leaf position
3201 result = true;
3202 }
3203 else if (ignorableRevocationStatusCode(scode)) {
3204 result = true;
3205 }
3206 else {
3207 result = false;
3208 break;
3209 }
3210 }
3211 return result;
3212}
3213
427c49bc
A
3214static OSStatus
3215_FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert)
b1ab9ed8
A
3216{
3217 CFDictionaryRef props = NULL;
3218 CFArrayRef keychains = NULL;
3219 CFArrayRef anchors = NULL;
3220 CFArrayRef certs = NULL;
3221 CFArrayRef chain = NULL;
3222 SecTrustRef trust = NULL;
3223
3224 SecTrustResultType trustResult;
3225 CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
3226 Boolean needChain = false;
3227 OSStatus status;
427c49bc 3228 if (!policy || !cert) return errSecParam;
b1ab9ed8
A
3229
3230 certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks);
3231 status = SecTrustCreateWithCertificates(certs, policy, &trust);
3232 if(status) goto cleanup;
3233
427c49bc
A
3234 /* Set evaluation date, if specified (otherwise current date is implied) */
3235 if (date && (CFGetTypeID(date) == CFDateGetTypeID())) {
3236 status = SecTrustSetVerifyDate(trust, date);
3237 if(status) goto cleanup;
3238 }
3239
b1ab9ed8
A
3240 /* Check whether this is the X509 Basic policy, which means chain building */
3241 props = SecPolicyCopyProperties(policy);
3242 if (props) {
3243 CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
3244 if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) {
3245 needChain = true;
3246 }
3247 }
3248
3249 if (!needChain) {
3250 /* To make the evaluation as lightweight as possible, specify an empty array
3251 * of keychains which will be searched for certificates.
3252 */
3253 keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3254 status = SecTrustSetKeychains(trust, keychains);
3255 if(status) goto cleanup;
3256
3257 /* To make the evaluation as lightweight as possible, specify an empty array
3258 * of trusted anchors.
3259 */
3260 anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3261 status = SecTrustSetAnchorCertificates(trust, anchors);
3262 if(status) goto cleanup;
3263 }
3264
3265 /* All parameters are locked and loaded, ready to evaluate! */
3266 status = SecTrustEvaluate(trust, &trustResult);
3267 if(status) goto cleanup;
3268
3269 /* If we didn't provide trust anchors or a way to look for them,
3270 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
3271 * However, we can tell whether the policy evaluation succeeded by
3272 * looking at the per-cert status codes in the returned evidence.
3273 */
3274 status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
3275 if(status) goto cleanup;
3276
3277 if (!(trustResult == kSecTrustResultProceed ||
3278 trustResult == kSecTrustResultUnspecified ||
3279 trustResult == kSecTrustResultRecoverableTrustFailure)) {
3280 /* The evaluation failed in a non-recoverable way */
3281 status = errSecCertificateCannotOperate;
3282 goto cleanup;
3283 }
3284
3285 /* If there are no per-cert policy status codes,
3286 * and the cert has not expired, consider it valid for the policy.
3287 */
3288 if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) &&
3289 ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) &&
3290 ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) {
427c49bc 3291 status = errSecSuccess;
b1ab9ed8
A
3292 }
3293 else {
3294 status = errSecCertificateCannotOperate;
3295 }
3296
3297cleanup:
3298 if(props) CFRelease(props);
3299 if(chain) CFRelease(chain);
3300 if(anchors) CFRelease(anchors);
3301 if(keychains) CFRelease(keychains);
3302 if(certs) CFRelease(certs);
3303 if(trust) CFRelease(trust);
3304
3305 return status;
3306}
3307
427c49bc 3308static OSStatus
b1ab9ed8
A
3309_FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert)
3310{
427c49bc 3311 if (!validOnDate || !cert) return errSecParam;
b1ab9ed8
A
3312
3313 CFAbsoluteTime at, nb, na;
3314 if (CFGetTypeID(validOnDate) == CFDateGetTypeID())
3315 at = CFDateGetAbsoluteTime((CFDateRef)validOnDate);
3316 else
3317 at = CFAbsoluteTimeGetCurrent();
3318
427c49bc
A
3319 OSStatus status = errSecSuccess;
3320 nb = SecCertificateNotValidBefore(cert);
3321 na = SecCertificateNotValidAfter(cert);
b1ab9ed8 3322
427c49bc 3323 if (nb == 0 || na == 0 || nb == na)
b1ab9ed8 3324 status = errSecCertificateCannotOperate;
427c49bc
A
3325 else if (at < nb)
3326 status = errSecCertificateNotValidYet;
3327 else if (at > na)
3328 status = errSecCertificateExpired;
b1ab9ed8 3329
b1ab9ed8
A
3330 return status;
3331}
3332
427c49bc 3333static OSStatus
b1ab9ed8
A
3334_FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert)
3335{
427c49bc
A
3336 if (!cert) return errSecParam;
3337 if (!trustedOnly) return errSecSuccess;
b1ab9ed8
A
3338
3339 CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks);
3340 SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic);
3341 OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess;
3342
3343 if (!status) {
3344 SecTrustRef trust = NULL;
3345 status = SecTrustCreateWithCertificates(certArray, policy, &trust);
3346 if (!status) {
3347 SecTrustResultType trustResult;
3348 status = SecTrustEvaluate(trust, &trustResult);
3349 if (!status) {
3350 if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) {
3351 status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted;
3352 }
3353 }
3354 CFRelease(trust);
3355 }
3356 CFRelease(policy);
3357 }
3358 if (certArray) {
3359 CFRelease(certArray);
3360 }
3361
3362 return status;
3363}
3364
427c49bc
A
3365static SecKeychainItemRef
3366CopyResolvedKeychainItem(CFTypeRef item)
3367{
3368 SecKeychainItemRef kcItem = NULL;
3369 OSStatus status;
3370 if (item) {
3371 if (CFGetTypeID(item) == CFDataGetTypeID()) {
3372 // persistent reference, resolve first
3373 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem);
3374 }
3375 else {
3376 // normal reference
3377 kcItem = (SecKeychainItemRef) CFRetain(item);
3378 }
3379 if (kcItem) {
3380 // ask for the item's class:
3381 // will return an error if the item has been deleted
3382 SecItemClass itemClass;
3383 SecKeychainItemRef certRef = NULL;
3384 if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) {
3385 status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef);
3386 }
3387 status = SecKeychainItemCopyAttributesAndData((certRef) ? certRef : kcItem, NULL, &itemClass, NULL, NULL, NULL);
3388 if (certRef) {
3389 CFRelease(certRef);
3390 }
3391 if (status) {
3392 CFRelease(kcItem);
3393 kcItem = NULL;
3394 }
3395 }
3396 }
3397 return kcItem;
3398}
3399
3400static OSStatus
b1ab9ed8
A
3401UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item)
3402{
3403 // This function refreshes the search parameters in the specific case where
3404 // the caller is searching for kSecClassKey items but did not provide the
3405 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3406 // we must perform separate searches to obtain all results.
3407
3408 OSStatus status = errSecItemNotFound;
3409 if (!params || !params->assumedKeyClass || !params->query || !item)
3410 return status;
3411
3412 // Free the previous search reference and attribute list.
3413 if (params->search)
3414 CFRelease(params->search);
3415 params->search = NULL;
3416 _FreeAttrList(params->attrList);
3417 params->attrList = NULL;
3418
3419 // Make a copy of the query dictionary so we can set the key class parameter.
3420 CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
3421 CFRelease(params->query);
3422 params->query = dict;
3423 CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
3424
3425 // Determine the current item class for this search, and the next assumed key class.
3426 if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) {
3427 params->itemClass = kSecSymmetricKeyItemClass;
3428 params->assumedKeyClass = kSecAttrKeyClassPublic;
3429 } else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) {
3430 params->itemClass = kSecPublicKeyItemClass;
3431 params->assumedKeyClass = kSecAttrKeyClassPrivate;
3432 } else {
3433 params->itemClass = kSecPrivateKeyItemClass;
3434 params->assumedKeyClass = NULL;
3435 }
3436
3437 // Rebuild the attribute list for the new key class.
427c49bc 3438 if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, &params->attrList) == errSecSuccess) {
b1ab9ed8
A
3439 // Create a new search reference for the new attribute list.
3440 if (SecKeychainSearchCreateFromAttributes(params->searchList,
3441 params->itemClass,
3442 (params->attrList->count == 0) ? NULL : params->attrList,
427c49bc 3443 (SecKeychainSearchRef*)&params->search) == errSecSuccess) {
b1ab9ed8
A
3444 // Return the first matching item from the new search.
3445 // We won't come back here again until there are no more matching items for this search.
3446 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
3447 }
3448 }
3449 return status;
3450}
3451
3452
427c49bc 3453static OSStatus
b1ab9ed8
A
3454SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
3455{
3456 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3457 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3458 // depending on the type of search reference.
3459
3460 OSStatus status;
3461 CFTypeRef search = (params) ? params->search : NULL;
3462 CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
3463 if (typeID == SecIdentitySearchGetTypeID()) {
3464 status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
3465 }
3466 else if (typeID == SecKeychainSearchGetTypeID()) {
3467 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
3468 // Check if we need to refresh the search for the next key class
3469 while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
3470 status = UpdateKeychainSearchAndCopyNext(params, item);
3471 }
427c49bc
A
3472 else if (typeID == 0 && (params->useItems || params->itemList)) {
3473 // No search available, but there is an item list available.
3474 // Return the next candidate item from the caller's item list
3475 CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList;
3476 CFIndex count = CFArrayGetCount(itemList);
3477 *item = (CFTypeRef) NULL;
3478 if (params->itemListIndex < count) {
3479 *item = (CFTypeRef)CFArrayGetValueAtIndex(itemList, params->itemListIndex++);
3480 if (*item) {
3481 // Potentially resolve persistent item references here, and
3482 // verify the item reference we're about to hand back is still
3483 // valid (it could have been deleted from the keychain while
3484 // our query was holding onto the itemList).
3485 *item = CopyResolvedKeychainItem(*item);
3486 if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) {
3487 // Persistent reference resolved to an identity, so return that type.
3488 params->returnIdentity = true;
3489 }
3490 }
3491 }
3492 status = (*item) ? errSecSuccess : errSecItemNotFound;
3493 }
b1ab9ed8
A
3494 else {
3495 status = errSecItemNotFound;
3496 }
3497 return status;
3498}
3499
427c49bc 3500static OSStatus
b1ab9ed8
A
3501FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity)
3502{
3503 if (!item || *item == NULL || !itemParams)
3504 return errSecItemNotFound;
3505
3506 OSStatus status;
3507 CFStringRef commonName = NULL;
3508 SecIdentityRef foundIdentity = NULL;
3509 if (CFGetTypeID(*item) == SecIdentityGetTypeID()) {
3510 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3511 // replace the found "item" with its associated certificate (which is the
3512 // item we actually want for purposes of getting attributes, data, or a
3513 // persistent data reference), and return the identity separately.
3514 SecCertificateRef certificate;
3515 status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate);
3516 if (itemParams->returnIdentity) {
3517 foundIdentity = (SecIdentityRef) *item;
3518 if (identity) {
3519 *identity = foundIdentity;
3520 }
3521 }
3522 else {
3523 CFRelease(*item);
3524 }
3525 *item = (CFTypeRef)certificate;
3526 }
3527
3528 CFDictionaryRef query = itemParams->query;
3529
3530 if (itemParams->itemClass == kSecCertificateItemClass) {
3531 // perform string comparisons first
3532 CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query);
3533 CFStringRef nameContains, nameStarts, nameEnds, nameExact;
3534 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains))
3535 nameContains = NULL;
3536 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts))
3537 nameStarts = NULL;
3538 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds))
3539 nameEnds = NULL;
3540 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact))
3541 nameExact = NULL;
3542 if (nameContains || nameStarts || nameEnds || nameExact) {
3543 status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName);
3544 if (status || !commonName) goto filterOut;
3545 }
3546 if (nameContains) {
3547 CFRange range = CFStringFind(commonName, nameContains, flags);
3548 if (range.length < 1)
3549 goto filterOut;
3550 // certificate item contains string; proceed to next check
3551 }
3552 if (nameStarts) {
3553 CFRange range = CFStringFind(commonName, nameStarts, flags);
3554 if (range.length < 1 || range.location > 1)
3555 goto filterOut;
3556 // certificate item starts with string; proceed to next check
3557 }
3558 if (nameEnds) {
3559 CFRange range = CFStringFind(commonName, nameEnds, flags);
3560 if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
3561 goto filterOut;
3562 // certificate item ends with string; proceed to next check
3563 }
3564 if (nameExact) {
3565 CFRange range = CFStringFind(commonName, nameExact, flags);
3566 if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
3567 goto filterOut;
3568 // certificate item exactly matches string; proceed to next check
3569 }
3570 if (itemParams->returnIdentity) {
3571 // if we already found and returned the identity, we can skip this
3572 if (!foundIdentity) {
3573 status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
3574 if (status) goto filterOut;
3575 }
3576 // certificate item is part of an identity; proceed to next check
3577 }
3578 if (itemParams->policy) {
427c49bc 3579 status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item);
b1ab9ed8 3580 if (status) goto filterOut;
427c49bc 3581 // certificate item is valid for specified policy (and optionally specified date)
b1ab9ed8
A
3582 }
3583 if (itemParams->validOnDate) {
3584 status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
3585 if (status) goto filterOut;
3586 // certificate item is valid for specified date
3587 }
3588 if (itemParams->trustedOnly) {
3589 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3590 // their trust has already been validated and we can skip this part.
3591 if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
3592 status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
3593 if (status) goto filterOut;
3594 }
3595 // certificate item is trusted on this system
3596 }
3597 }
3598 if (itemParams->itemList) {
3599 Boolean foundMatch = FALSE;
3600 CFIndex idx, count = CFArrayGetCount(itemParams->itemList);
3601 for (idx=0; idx<count; idx++) {
3602 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx);
3603 SecKeychainItemRef realItem = NULL;
3604 SecCertificateRef aCert = NULL;
3605 if (anItem == NULL) {
3606 continue;
3607 }
3608 if (CFDataGetTypeID() == CFGetTypeID(anItem) &&
427c49bc 3609 errSecSuccess == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) {
b1ab9ed8
A
3610 anItem = realItem;
3611 }
3612 if (SecIdentityGetTypeID() == CFGetTypeID(anItem) &&
427c49bc 3613 errSecSuccess == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) {
b1ab9ed8
A
3614 anItem = aCert;
3615 }
3616 if (CFEqual(anItem, (CFTypeRef) *item)) {
3617 foundMatch = TRUE;
3618 }
3619 if (aCert) {
3620 CFRelease(aCert);
3621 }
3622 if (realItem) {
3623 CFRelease(realItem);
3624 }
3625 if (foundMatch) {
3626 break;
3627 }
3628 }
3629 if (!foundMatch) goto filterOut;
3630 // item was found on provided list
3631 }
3632
3633 if (foundIdentity && !identity) {
3634 CFRelease(foundIdentity);
3635 }
3636 if (commonName) {
3637 CFRelease(commonName);
3638 }
3639
3640 // if we get here, consider the item a match
427c49bc 3641 return errSecSuccess;
b1ab9ed8
A
3642
3643filterOut:
3644 if (commonName) {
3645 CFRelease(commonName);
3646 }
3647 CFRelease(*item);
3648 *item = NULL;
3649 if (foundIdentity) {
3650 CFRelease(foundIdentity);
3651 if (identity) {
3652 *identity = NULL;
3653 }
3654 }
3655 return errSecItemNotFound;
3656}
3657
427c49bc 3658static OSStatus
b1ab9ed8
A
3659AddItemResults(SecKeychainItemRef item,
3660 SecIdentityRef identity,
3661 SecItemParams *itemParams,
3662 CFAllocatorRef allocator,
3663 CFMutableArrayRef *items,
3664 CFTypeRef *result)
3665{
3666 // Given a found item (which may also be an identity), this function adds
3667 // the requested result types (specified in itemParams) to the appropriate
3668 // container as follows:
3669 //
3670 // 1. If there is only one result type (numResultTypes == 1) and only one
3671 // match requested (maxMatches == 1), set *result directly.
3672 //
3673 // 2. If there are multiple result types (numResultTypes > 1), and only one
3674 // match requested (maxMatches == 1), add each result type to itemDict
3675 // and set itemDict as the value of *result.
3676 //
3677 // 3. If there is only one result type (numResultTypes == 1) and multiple
3678 // possible matches (maxMatches > 1), add the result type to *items
3679 // and set *items as the value of *result.
3680 //
3681 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3682 // possible matches (maxMatches > 1), add each result type to itemDict,
3683 // add itemDict to *items, and set *items as the value of *result.
3684 //
3685 // Note that we allocate *items if needed.
3686
3687 if (!item || !itemParams || !result)
427c49bc 3688 return errSecParam;
b1ab9ed8
A
3689
3690 if (itemParams->maxMatches > 1) {
3691 // if we can return more than one item, we must have an array
3692 if (!items)
427c49bc 3693 return errSecParam;
b1ab9ed8
A
3694 else if (*items == NULL)
3695 *items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
3696 }
3697
427c49bc 3698 OSStatus tmpStatus, status = errSecSuccess;
b1ab9ed8
A
3699 CFMutableArrayRef itemArray = (items) ? *items : NULL;
3700 CFMutableDictionaryRef itemDict = NULL;
3701 if (itemParams->numResultTypes > 1) {
3702 // if we're returning more than one result type, each item we return must be a dictionary
3703 itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3704 }
3705
3706 if (itemParams->returningRef) {
3707 const void* itemRef = (identity) ? (const void*)identity : (const void*)item;
3708 if (itemDict) {
3709 CFDictionaryAddValue(itemDict, kSecValueRef, itemRef);
3710 }
3711 else if (itemArray) {
3712 CFArrayAppendValue(itemArray, itemRef);
3713 }
3714 else {
3715 *result = CFRetain((CFTypeRef)itemRef);
3716 }
3717 }
3718
3719 if (itemParams->returningPersistentRef) {
3720 CFDataRef persistentRef;
427c49bc
A
3721 SecKeychainItemRef tmpItem = item;
3722 if (itemParams->identityRef) {
3723 tmpItem = (SecKeychainItemRef)itemParams->identityRef;
3724 }
3725 tmpStatus = SecKeychainItemCreatePersistentReference(tmpItem, &persistentRef);
3726 if (tmpStatus == errSecSuccess) {
b1ab9ed8
A
3727 if (itemDict) {
3728 CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef);
3729 }
3730 else if (itemArray) {
3731 CFArrayAppendValue(itemArray, persistentRef);
3732 }
3733 else {
3734 *result = CFRetain(persistentRef);
3735 }
3736 CFRelease(persistentRef);
3737 }
427c49bc 3738 else if (status == errSecSuccess) {
b1ab9ed8
A
3739 status = tmpStatus;
3740 }
3741 }
3742
3743 if (itemParams->returningData) {
3744 UInt32 length;
3745 void *data;
3746 tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data);
427c49bc 3747 if (tmpStatus == errSecSuccess) {
b1ab9ed8
A
3748 CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length);
3749 if (itemDict) {
3750 CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
3751 }
3752 else if (itemArray) {
3753 CFArrayAppendValue(itemArray, dataRef);
3754 }
3755 else {
3756 *result = CFRetain(dataRef);
3757 }
3758 CFRelease(dataRef);
3759 (void) SecKeychainItemFreeContent(NULL, data);
3760 }
427c49bc 3761 else if (status == errSecSuccess) {
b1ab9ed8
A
3762 status = tmpStatus;
3763 }
3764 }
3765
3766 if (itemParams->returningAttributes) {
3767 CFDictionaryRef attrsDict = NULL;
3768 SecItemClass itemClass;
3769 // since we have an item, allow its actual class to override the query-specified item class
3770 tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
3771 if (tmpStatus) {
3772 itemClass = itemParams->itemClass;
3773 }
3774 tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
3775 if (attrsDict) {
3776 if (itemDict) {
3777 // add all keys and values from attrsDict to the item dictionary
3778 CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict);
3779 }
3780 else if (itemArray) {
3781 CFArrayAppendValue(itemArray, attrsDict);
3782 }
3783 else {
3784 *result = CFRetain(attrsDict);
3785 }
3786 CFRelease(attrsDict);
3787 }
427c49bc 3788 if (tmpStatus && (status == errSecSuccess)) {
b1ab9ed8
A
3789 status = tmpStatus;
3790 }
3791 }
3792
3793 if (itemDict) {
3794 if (itemArray) {
3795 CFArrayAppendValue(itemArray, itemDict);
3796 CFRelease(itemDict);
3797 *result = itemArray;
3798 }
3799 else {
3800 *result = itemDict;
3801 }
3802 }
3803 else if (itemArray) {
3804 *result = itemArray;
3805 }
3806
3807 return status;
3808}
3809
427c49bc
A
3810CFDataRef _SecItemGetPersistentReference(CFTypeRef raw_item)
3811{
3812 try {
3813 Item item = ItemImpl::required((SecKeychainItemRef)raw_item);
3814 return item->getPersistentRef();
3815 } catch(...) {
3816 return NULL;
3817 }
3818}
b1ab9ed8
A
3819
3820/******************************************************************************/
3821#pragma mark SecItem API functions
3822/******************************************************************************/
3823
427c49bc
A
3824//
3825// Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3826//
3827static SInt32 readNumber(CFTypeRef obj) {
3828 CFTypeID tid = CFGetTypeID(obj);
3829 SInt32 v = 0;
3830 if (tid == CFNumberGetTypeID()) {
3831 CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt32Type, &v);
3832 return v;
3833 } else if (tid == CFBooleanGetTypeID()) {
3834 v = CFBooleanGetValue((CFBooleanRef)obj);
3835 return v;
3836 } else if (tid == CFStringGetTypeID()) {
3837 v = CFStringGetIntValue((CFStringRef)obj);
3838 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v);
3839 /* If a string converted to an int isn't equal to the int printed as
3840 a string, return a CFStringRef instead. */
3841 if (!CFEqual(t, obj)) {
3842 CFRelease(t);
3843 return 0;
3844 }
3845 CFRelease(t);
3846 return v;
3847 } else
3848 return NULL;
3849}
3850
3851//
3852// Function to ensure the syncable keychain is unlocked.
3853// Currently, this means unlocking the login keychain,
3854// which will also unlock the keybag as a side effect.
3855//
3856static OSStatus SecItemUnlockSynchronizableKeychain()
3857{
3858 SecKeychainRef keychain = NULL;
3859 OSStatus status = SecKeychainCopyLogin(&keychain);
3860 if (!status) {
3861 status = SecKeychainUnlock(keychain, 0, NULL, false);
3862 }
3863 CFReleaseSafe(keychain);
3864 return status;
3865}
3866
3867//
3868// Function to check whether the kSecAttrSynchronizable flag is set in the query.
3869//
b1ab9ed8
A
3870static Boolean SecItemSynchronizable(CFDictionaryRef query)
3871{
427c49bc
A
3872 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
3873 Boolean result = (value && readNumber(value));
b1ab9ed8 3874
427c49bc
A
3875 return result;
3876}
b1ab9ed8 3877
427c49bc
A
3878//
3879// Function to check whether the kSecAttrSynchronizable flag is set in the query,
3880// and has the special value of kSecAttrSynchronizableAny.
3881//
3882static Boolean SecItemSynchronizableAny(CFDictionaryRef query)
3883{
3884 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
3885 if (value) {
3886 return (CFGetTypeID(value) == CFStringGetTypeID() &&
3887 CFEqual(value, kSecAttrSynchronizableAny));
3888 }
3889 return false;
3890}
3891
3892//
3893// Function to check whether the kSecAttrSynchronizable attribute is being updated.
3894//
3895static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictionaryRef changes)
3896{
3897 CFTypeRef newValue = CFDictionaryGetValue(changes, kSecAttrSynchronizable);
3898 if (!newValue)
3899 return false;
3900
3901 Boolean new_sync = readNumber(newValue);
3902 Boolean old_sync = synchronizable;
3903
3904 return (old_sync != new_sync);
3905}
b1ab9ed8 3906
427c49bc
A
3907//
3908// Returns true if keychain syncing is globally enabled.
3909//
3910static Boolean SecItemSyncEnabled()
3911{
3912 static dispatch_once_t onceToken;
3913 static Boolean syncEnabled = true;
3914
3915 //sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES
3916 dispatch_once(&onceToken, ^{
3917 CFTypeRef sync = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
3918
3919 if (sync && CFGetTypeID(sync) == CFBooleanGetTypeID()) {
3920 syncEnabled = CFBooleanGetValue((CFBooleanRef)sync);
3921 CFRelease(sync);
3922 }
3923 });
3924
3925 return syncEnabled;
3926}
3927
3928//
3929// Function to check whether a synchronizable persistent reference was provided.
3930//
3931static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query)
3932{
3933 CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef);
3934 if (value) {
3935 /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
3936 const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
3937 return (CFGetTypeID(value) == CFDataGetTypeID() &&
3938 CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
3939 }
3940 return false;
3941}
3942
3943//
3944// Function to apply changes to a mutable dictionary.
3945// (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
3946//
3947static void SecItemApplyChanges(const void *key, const void *value, void *context)
3948{
3949 CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context;
3950 if (!dict) return;
3951
3952 CFDictionarySetValue(dict, key, value);
3953}
3954
3955//
3956// Function to change matching items from non-syncable to syncable
3957// (if toSyncable is true), otherwise from syncable to non-syncable.
3958// This currently moves items between keychain containers.
3959//
3960static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable)
3961{
3962 // Note: the input query dictionary is a mutable copy of the query originally
3963 // provided by the caller as the first parameter to SecItemUpdate. It may not
3964 // specify returning attributes or data, but we will need both to make a copy.
3965 //
3966 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnRef);
3967 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnPersistentRef);
3968 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnData);
3969 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnAttributes, kCFBooleanTrue);
3970 if (NULL == CFDictionaryGetValue(changes, kSecValueData))
3971 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnData, kCFBooleanTrue);
3972
3973 CFTypeRef result;
3974 OSStatus status;
3975 if (toSyncable)
3976 status = SecItemCopyMatching_osx(query, &result);
3977 else
3978 status = SecItemCopyMatching_ios(query, &result);
3979
3980 if (status)
3981 return status;
3982 if (!result)
3983 return errSecItemNotFound;
3984
3985 CFMutableArrayRef items;
3986 if (CFGetTypeID(result) != CFArrayGetTypeID()) {
3987 items = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
3988 CFArrayAppendValue(items, result);
3989 CFRelease(result);
3990 }
3991 else {
3992 items = (CFMutableArrayRef)result;
3993 }
3994
3995 CFIndex idx, count = (items) ? CFArrayGetCount(items) : 0;
3996 int priority = LOG_DEBUG;
3997 OSStatus err = 0;
3998 for (idx = 0; idx < count; idx++) {
3999 CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(items, idx);
4000 CFMutableDictionaryRef item = (CFMutableDictionaryRef)
4001 SecItemCopyTranslatedAttributes(dict,
4002 CFDictionaryGetValue(query, kSecClass),
4003 (toSyncable) ? true : false /*iOSOut*/,
4004 true /*pruneMatch*/,
4005 true /*pruneSync*/,
4006 true /*pruneReturn*/,
4007 false /*pruneData*/,
4008 (toSyncable) ? true : false /*pruneAccess*/);
4009 // hold onto the query before applying changes, in case the item already exists.
4010 // note that we cannot include the creation or modification dates from our
4011 // found item in this query, as they may not match the item in the other keychain.
4012 CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item);
4013 CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate);
4014 CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate);
4015 // apply changes to the item dictionary that we will pass to SecItemAdd
4016 CFDictionaryApplyFunction(changes, SecItemApplyChanges, item);
4017 if (toSyncable) {
4018 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
4019 status = SecItemAdd_ios(item, NULL);
4020 secitemlog(priority, "ChangeSync: SecItemAdd_ios=%d", status);
4021 if (errSecDuplicateItem == status) {
4022 // find and apply changes to the existing syncable item.
4023 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
4024 status = SecItemUpdate_ios(itemQuery, changes);
4025 secitemlog(priority, "ChangeSync: SecItemUpdate_ios=%d", status);
4026 }
4027 if (errSecSuccess == status) {
4028 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
4029 status = SecItemDelete_osx(itemQuery);
4030 secitemlog(priority, "ChangeSync: SecItemDelete_osx=%d", status);
4031 }
4032 }
4033 else {
4034 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
4035 status = SecItemAdd_osx(item, NULL);
4036 secitemlog(priority, "ChangeSync: SecItemAdd_osx=%d", status);
4037 if (errSecDuplicateItem == status) {
4038 // find and apply changes to the existing non-syncable item.
4039 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
4040 status = SecItemUpdate_osx(itemQuery, changes);
4041 secitemlog(priority, "ChangeSync: SecItemUpdate_osx=%d", status);
4042 }
4043 if (errSecSuccess == status) {
4044 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
4045 status = SecItemDelete_ios(itemQuery);
4046 secitemlog(priority, "ChangeSync: SecItemDelete_ios=%d", status);
4047 }
4048 }
4049 CFReleaseSafe(item);
4050 CFReleaseSafe(itemQuery);
4051 if (status)
4052 err = status;
4053 }
4054 CFReleaseSafe(items);
4055
4056 return err;
4057}
4058
4059
4060extern "C" {
4061
4062CFTypeRef
4063SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
4064 CFTypeRef ref = NULL;
4065 CFStringRef key_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass);
4066 SecItemClass key_class;
4067 bool key_class_found = false;
4068
4069 if (CFEqual(key_class_string, kSecClassGenericPassword)) {
4070 key_class = kSecGenericPasswordItemClass;
4071 key_class_found = true;
4072 }
4073 if (CFEqual(key_class_string, kSecClassInternetPassword)) {
4074 key_class = kSecInternetPasswordItemClass;
4075 key_class_found = true;
4076 }
4077
4078 if (key_class_found) {
4079 // we carry v_Data around here so the *_ios calls can find it and locate
4080 // their own data. Putting things in the attribute list doesn't help as
4081 // the osx keychainitem and item calls bail when they don't see a keychain
4082 // object. If we need to make them work we either have to bridge them, or
4083 // find a way to craft a workable keychain object. #if'ed code left below
4084 // in case we need to go down that path.
4085
4086 struct SecKeychainAttributeList *attrs = (struct SecKeychainAttributeList *)malloc(sizeof(struct SecKeychainAttributeList) + sizeof(struct SecKeychainAttribute) * 0);
4087 attrs->attr = (struct SecKeychainAttribute *)(attrs + 1);
4088 attrs->count = 0;
4089 CFTypeRef v;
4090#if 0
4091 // The C++ string objects need to last at least as long as the attr struct.
4092 string account;
4093
4094 v = CFDictionaryGetValue(refAttributes, CFSTR("mdat"));
4095 if (v) {
4096 attrs->attr[attrs->count].tag = kSecModDateItemAttr;
4097 // XXX need to convert to YYYYMMDDhhmmSSZ
4098 attrs->attr[attrs->count].data = (void*)"19690223140232Z";
4099 attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
4100 attrs->count++;
4101 }
4102 v = CFDictionaryGetValue(refAttributes, CFSTR("cdat"));
4103 if (v) {
4104 attrs->attr[attrs->count].tag = kSecCreationDateItemAttr;
4105 // XXX need to convert to YYYYMMDDhhmmSSZ
4106 attrs->attr[attrs->count].data = (void*)"19690223140232Z";
4107 attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
4108 attrs->count++;
4109 }
4110
4111 v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
4112 if (v) {
4113 attrs->attr[attrs->count].tag = kSecAccountItemAttr;
4114 account = cfString((CFStringRef)v);
4115 attrs->attr[attrs->count].data = (void*)(account.c_str());
4116 attrs->attr[attrs->count].length = account.length();
4117 attrs->count++;
4118 }
4119
4120 // class isn't treated as an attribute by the creation API
4121
4122 v = CFDictionaryGetValue(refAttributes, CFSTR("svce"));
4123 if (v) {
4124 attrs->attr[attrs->count].tag = kSecServiceItemAttr;
4125 account = cfString((CFStringRef)v);
4126 attrs->attr[attrs->count].data = (void*)(account.c_str());
4127 attrs->attr[attrs->count].length = account.length();
4128 attrs->count++;
4129 }
4130
4131 v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
4132 if (v) {
4133 attrs->attr[attrs->count].tag = kSecLabelItemAttr;
4134 account = cfString((CFStringRef)v);
4135 attrs->attr[attrs->count].data = (void*)(account.c_str());
4136 attrs->attr[attrs->count].length = account.length();
4137 attrs->count++;
4138 }
4139#endif
4140 Item item = Item(key_class, attrs, 0, "");
4141 ItemImpl *real_item = item.get();
4142 v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
4143 if (v) {
4144 real_item->setPersistentRef((CFDataRef)v);
4145 }
4146 ref = real_item->handle();
4147 } else {
4148 // keys, certs, identities are not currently sync'able.
4149 ref = NULL;
4150 }
4151 return ref;
4152}
4153
4154/*
4155 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4156 * is a member of the specified application group, and is signed by Apple.
4157 */
4158OSStatus
4159SecItemValidateAppleApplicationGroupAccess(CFStringRef group)
4160{
4161 SecTrustedApplicationRef app = NULL;
4162 SecRequirementRef requirement = NULL;
4163 SecCodeRef code = NULL;
4164 OSStatus status = errSecParam;
4165
4166 if (group) {
4167 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(group), kCFStringEncodingUTF8) + 1;
4168 char* buffer = (char*) malloc(length);
4169 if (buffer) {
4170 if (CFStringGetCString(group, buffer, length, kCFStringEncodingUTF8)) {
4171 status = SecTrustedApplicationCreateApplicationGroup(buffer, NULL, &app);
4172 }
4173 free(buffer);
4174 } else {
4175 status = errSecMemoryError;
4176 }
4177 }
4178 if (!status) {
4179 status = SecTrustedApplicationCopyRequirement(app, &requirement);
4180 }
4181 if (!status) {
4182 status = SecCodeCopySelf(kSecCSDefaultFlags, &code);
4183 }
4184 if (!status) {
4185 status = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement);
4186 }
4187
4188 CFReleaseSafe(code);
4189 CFReleaseSafe(requirement);
4190 CFReleaseSafe(app);
4191 return status;
4192}
4193
4194/*
4195 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4196 * and attempts to return a sanitized copy for passing to the underlying
4197 * platform-specific implementation code.
4198 *
4199 * If iOSOut is true, one or more translations may apply:
4200 * - SecKeychain refs are removed, since there aren't multiple keychains
4201 * - SecPolicy refs are removed, since they can't be externalized
4202 * - SecAccess refs are removed, and potentially translated to entitlements
4203 *
4204 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4205 * parameter errors due to strict input checks in secd, which only permits
4206 * these constants for calls to SecItemCopyMatching.
4207 *
4208 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4209 * This permits a query to be reused for non-synchronizable items, or to
4210 * resolve a search based on a persistent item reference for iOS.
4211 *
4212 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4213 * parameter errors due to strict input checks in secd, which do not permit
4214 * these constants for calls to SecItemUpdate.
4215 */
4216CFDictionaryRef
4217SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
4218 bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess)
4219{
4220 CFMutableDictionaryRef result = CFDictionaryCreateMutableCopy(NULL, 0, inOSXDict);
4221 if (result == NULL) {
4222 return result;
4223 }
4224
4225 if (pruneSync) {
4226 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4227 }
4228
4229 if (pruneMatch) {
4230 /* Match constants are only supported on iOS for SecItemCopyMatching,
4231 * and will generate an error if passed to other SecItem API functions;
4232 * on OS X, they're just ignored if not applicable for the context.
4233 */
4234 CFDictionaryRemoveValue(result, kSecMatchPolicy);
4235 CFDictionaryRemoveValue(result, kSecMatchItemList);
4236 CFDictionaryRemoveValue(result, kSecMatchSearchList);
4237 CFDictionaryRemoveValue(result, kSecMatchIssuers);
4238 CFDictionaryRemoveValue(result, kSecMatchEmailAddressIfPresent);
4239 CFDictionaryRemoveValue(result, kSecMatchSubjectContains);
4240 CFDictionaryRemoveValue(result, kSecMatchCaseInsensitive);
4241 CFDictionaryRemoveValue(result, kSecMatchTrustedOnly);
4242 CFDictionaryRemoveValue(result, kSecMatchValidOnDate);
4243 CFDictionaryRemoveValue(result, kSecMatchLimit);
4244 CFDictionaryRemoveValue(result, kSecMatchLimitOne);
4245 CFDictionaryRemoveValue(result, kSecMatchLimitAll);
4246 }
4247
4248 if (pruneReturn) {
4249 /* Return constants are not supported on iOS for SecItemUpdate,
4250 * where they will generate an error; on OS X, they're just ignored
4251 * if not applicable for the context.
4252 */
4253 CFDictionaryRemoveValue(result, kSecReturnData);
4254 CFDictionaryRemoveValue(result, kSecReturnAttributes);
4255 CFDictionaryRemoveValue(result, kSecReturnRef);
4256 CFDictionaryRemoveValue(result, kSecReturnPersistentRef);
4257 }
4258
4259 if (pruneData) {
4260 /* Searching on data is not supported. */
4261 CFDictionaryRemoveValue(result, kSecValueData);
4262 }
4263
4264 if (pruneAccess) {
4265 /* Searching on access lists is not supported */
4266 CFDictionaryRemoveValue(result, kSecAttrAccess);
4267 }
4268
4269 if (iOSOut) {
4270 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4271 * cannot specify a keychain search list on iOS
4272 */
4273 CFDictionaryRemoveValue(result, kSecMatchSearchList);
4274
4275 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4276 * cannot specify a keychain on iOS
4277 */
4278 CFDictionaryRemoveValue(result, kSecUseKeychain);
4279
4280 /* Remove kSecMatchPolicy (value is a SecPolicyRef);
4281 * TODO: need a way to externalize and restore a policy instance
4282 */
4283 CFDictionaryRemoveValue(result, kSecMatchPolicy);
4284
4285 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4286 * unless kSecAttrAccessGroup has already been specified.
4287 */
4288 SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess);
4289 CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup);
4290 if (access != NULL && accessGroup == NULL) {
4291 /* Translate "InternetAccounts" application group to an access group */
4292 if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4293 /* The caller is a valid member of the application group. */
4294 CFStringRef groupName = CFSTR("appleaccount");
4295 CFTypeRef value = CFDictionaryGetValue(result, kSecAttrAuthenticationType);
4296 if (value && CFEqual(value, kSecAttrAuthenticationTypeHTMLForm)) {
4297 groupName = CFSTR("com.apple.cfnetwork");
4298 }
4299 CFDictionarySetValue(result, kSecAttrAccessGroup, groupName);
4300 }
4301 }
4302 CFDictionaryRemoveValue(result, kSecAttrAccess);
4303
4304 /* If item is specified by direct reference, and this is an iOS search,
4305 * replace it with a persistent reference.
4306 */
4307 CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef);
4308 if (directRef) {
4309 CFDataRef persistentRef = _SecItemGetPersistentReference(directRef);
4310 if (persistentRef) {
4311 CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef);
4312 }
4313 CFDictionaryRemoveValue(result, kSecValueRef);
4314 }
4315
4316 /* If item is specified by persistent reference, and this is an iOS search,
4317 * remove the synchronizable attribute as it will be rejected by secd.
4318 */
4319 CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef);
4320 if (persistentRef) {
4321 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4322 }
4323
4324 /* Remove kSecAttrModificationDate; this should never be used as criteria
4325 * for a search, or to add/modify an item. (If we are cloning an item
4326 * and want to keep its modification date, we don't call this function.)
4327 * It turns out that some clients are using the full attributes dictionary
4328 * returned by SecItemCopyMatching as a query to find the same item later,
4329 * which won't work once the item is updated.
4330 */
4331 CFDictionaryRemoveValue(result, kSecAttrModificationDate);
b1ab9ed8 4332 }
427c49bc
A
4333 else {
4334 /* iOS doesn't add the class attribute, so we must do it here. */
4335 if (itemClass)
4336 CFDictionarySetValue(result, kSecClass, itemClass);
b1ab9ed8 4337
427c49bc
A
4338 /* Remove attributes which are not part of the OS X database schema. */
4339 CFDictionaryRemoveValue(result, kSecAttrAccessible);
4340 CFDictionaryRemoveValue(result, kSecAttrAccessGroup);
4341 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4342 CFDictionaryRemoveValue(result, kSecAttrTombstone);
4343 }
4344
4345 return result;
4346}
4347
4348/*
4349 * SecItemCopyMergedResults takes two input objects, which may be containers,
4350 * and returns a retained object which merges the results. Merging depends on the
4351 * result type. If each result is valid and is not an array, then only one match was
4352 * requested; in that case, the syncable (ios) match is preferred.
4353 *
4354 * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a
4355 * particular keychain to search, we do not want to merge in any IOS results. Also, may need
4356 * to filter out duplicates if two items differ only in the sync attribute.
4357 */
4358CFTypeRef
4359SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios)
4360{
4361 CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0;
4362 CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0;
4363 CFTypeID id_array = CFArrayGetTypeID();
4364 if ((id_osx == id_array) && (id_ios == id_array)) {
4365 // Fold the arrays into one.
4366 CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4367 CFArrayAppendArray(results, (CFArrayRef)result_ios, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios)));
4368 CFArrayAppendArray(results, (CFArrayRef)result_osx, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx)));
4369 return results;
4370 }
4371 // Result type is not an array, so only one match can be returned.
4372 return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx);
b1ab9ed8
A
4373}
4374
427c49bc
A
4375} /* extern "C" */
4376
4377
b1ab9ed8
A
4378OSStatus
4379SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
4380{
427c49bc
A
4381 secitemlog(LOG_NOTICE, "SecItemCopyMatching");
4382 if (!query) {
4383 return errSecParam;
4384 }
4385 secitemshow(query, "SecItemCopyMatching query:");
4386
4387 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4388 CFTypeRef result_osx = NULL, result_ios = NULL;
4389 Boolean sync_enabled = SecItemSyncEnabled();
4390 Boolean search_ios = SecItemSynchronizable(query);
4391 Boolean merge_search = SecItemSynchronizableAny(query);
4392 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4393
4394 if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4395 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4396 CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true);
4397 if (!attrs_ios) {
4398 status_ios = errSecParam;
4399 }
4400 else {
4401 SecItemUnlockSynchronizableKeychain();
4402 status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
4403 CFRelease(attrs_ios);
4404 }
4405 secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios);
4406 if (!merge_search || persistref_ios) {
4407 AssignOrReleaseResult(result_ios, result);
4408 return status_ios; // no need to search non-syncable keychains
4409 }
4410 }
b1ab9ed8 4411
427c49bc
A
4412 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4413 CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true);
4414 if (!attrs_osx) {
4415 status_osx = errSecParam;
4416 }
4417 else {
4418 status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx);
4419 CFRelease(attrs_osx);
4420 }
4421 secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx);
4422
4423 // If one of the searches failed to occur or produce results, we can eliminate it
4424 if (result_ios == NULL) {
4425 AssignOrReleaseResult(result_osx, result);
4426 return status_osx; // we can only have non-syncable results
4427 }
4428 if (result_osx == NULL) {
4429 AssignOrReleaseResult(result_ios, result);
4430 return status_ios; // we can only have syncable results
4431 }
b1ab9ed8 4432
427c49bc
A
4433 // If we get here, need to merge results
4434 CFTypeRef result_merged = SecItemCopyMergedResults(query, result_osx, result_ios);
4435 CFReleaseSafe(result_osx);
4436 CFReleaseSafe(result_ios);
4437 AssignOrReleaseResult(result_merged, result);
4438
4439 if (status_osx == status_ios) {
4440 return status_osx; // both searches produced the same result
4441 }
4442 else if (!status_osx || !status_ios) {
4443 return errSecSuccess; // one of the searches succeeded
4444 }
4445 else if (status_osx == errSecItemNotFound) {
4446 return status_ios; // this failure was more interesting
4447 }
4448 return status_osx;
b1ab9ed8
A
4449}
4450
4451OSStatus
4452SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
4453{
427c49bc
A
4454 secitemlog(LOG_NOTICE, "SecItemAdd");
4455 if (!attributes) {
4456 return errSecParam;
4457 }
4458 else if (result) {
b1ab9ed8 4459 *result = NULL;
427c49bc
A
4460 }
4461 secitemshow(attributes, "SecItemAdd attrs:");
b1ab9ed8 4462
427c49bc
A
4463 OSStatus status_osx, status_ios;
4464 CFTypeRef result_osx = NULL, result_ios = NULL;
4465 Boolean sync_enabled = SecItemSyncEnabled();
4466 Boolean add_ios = SecItemSynchronizable(attributes);
4467
4468 if (sync_enabled && add_ios) {
4469 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes,
4470 NULL, true, true, false, false, false, false);
4471 if (!attrs_ios) {
4472 status_ios = errSecParam;
4473 }
4474 else {
4475 SecItemUnlockSynchronizableKeychain();
4476 status_ios = SecItemAdd_ios(attrs_ios, &result_ios);
4477 CFRelease(attrs_ios);
4478 }
4479 secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status_ios);
4480 if (result)
4481 *result = result_ios;
4482 else
4483 CFReleaseSafe(result_ios);
4484 return status_ios;
4485 }
b1ab9ed8 4486
427c49bc
A
4487 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes,
4488 NULL, false, false, true, false, false, false);
4489 if (!attrs_osx) {
4490 status_osx = errSecParam;
4491 }
4492 else {
4493 status_osx = SecItemAdd_osx(attrs_osx, &result_osx);
4494 CFRelease(attrs_osx);
4495 }
4496 secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status_osx);
4497 if (result)
4498 *result = result_osx;
4499 else
4500 CFReleaseSafe(result_osx);
4501 return status_osx;
b1ab9ed8
A
4502}
4503
4504OSStatus
4505SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
4506{
427c49bc
A
4507 secitemlog(LOG_NOTICE, "SecItemUpdate");
4508 if (!query || !attributesToUpdate) {
4509 return errSecParam;
4510 }
4511 secitemshow(query, "SecItemUpdate query:");
4512 secitemshow(attributesToUpdate, "SecItemUpdate attrs:");
4513
4514 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4515 Boolean sync_enabled = SecItemSyncEnabled();
4516 Boolean search_ios = SecItemSynchronizable(query);
4517 Boolean merge_search = SecItemSynchronizableAny(query);
4518 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4519
4520 if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4521 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4522 CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true);
4523 if (!attrs_ios) {
4524 status_ios = errSecParam;
4525 }
4526 else {
4527 SecItemUnlockSynchronizableKeychain();
4528 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate))
4529 status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
4530 else
4531 status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
4532 CFRelease(attrs_ios);
4533 }
4534 secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios);
4535 if (!merge_search || persistref_ios)
4536 return status_ios;
4537 }
b1ab9ed8 4538
427c49bc
A
4539 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4540 CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true);
4541 if (!attrs_osx) {
4542 status_osx = errSecParam;
4543 }
4544 else {
4545 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate))
4546 status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true);
4547 else
4548 status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate);
b1ab9ed8 4549
427c49bc
A
4550 CFRelease(attrs_osx);
4551 }
4552 secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx);
4553 if (merge_search) {
4554 // Harmonize the result of the update attempts.
4555 if (status_osx == status_ios) {
4556 // both updates produced the same result
4557 return status_ios;
4558 }
4559 else if (!status_osx || !status_ios) {
4560 // one of the updates succeeded, but the other failed
4561 if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
4562 return errSecSuccess; // item only found in one keychain
4563 else
4564 return (status_osx) ? status_osx : status_ios; // return the error
4565 }
4566 else if (status_osx == errSecItemNotFound) {
4567 // both updates failed, status_ios failure is more interesting
4568 // since the item was actually found
4569 return status_ios;
4570 }
4571 }
4572 return status_osx;
b1ab9ed8
A
4573}
4574
4575OSStatus
4576SecItemDelete(CFDictionaryRef query)
4577{
427c49bc
A
4578 secitemlog(LOG_NOTICE, "SecItemDelete");
4579 if (!query) {
4580 return errSecParam;
4581 }
4582 secitemshow(query, "SecItemDelete query:");
4583
4584 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4585 Boolean sync_enabled = SecItemSyncEnabled();
4586 Boolean search_ios = SecItemSynchronizable(query);
4587 Boolean merge_search = SecItemSynchronizableAny(query);
4588 Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4589
4590 if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4591 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4592 NULL, true, true, false, true, true, true);
4593 if (!attrs_ios) {
4594 status_ios = errSecParam;
4595 }
4596 else {
4597 SecItemUnlockSynchronizableKeychain();
4598 status_ios = SecItemDelete_ios(attrs_ios);
4599 CFRelease(attrs_ios);
4600 }
4601 secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios);
4602 if (!merge_search || persistref_ios)
4603 return status_ios;
4604 }
b1ab9ed8 4605
427c49bc
A
4606 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4607 NULL, false, false, true, true, true, true);
4608 if (!attrs_osx) {
4609 status_osx = errSecParam;
4610 }
4611 else {
4612 status_osx = SecItemDelete_osx(attrs_osx);
4613 CFRelease(attrs_osx);
4614 }
4615 secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx);
b1ab9ed8 4616
427c49bc
A
4617 if (merge_search) {
4618 // Harmonize the result of the delete attempts.
4619 if (status_osx == status_ios) {
4620 // both deletes produced the same result
4621 return status_ios;
4622 }
4623 else if (!status_osx || !status_ios) {
4624 // one of the deletes succeeded, but the other failed
4625 if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
4626 return errSecSuccess; // item only found in one keychain
4627 else
4628 return (status_osx) ? status_osx : status_ios; // return the error
4629 }
4630 else if (status_osx == errSecItemNotFound) {
4631 // both deletes failed, status_ios failure is more interesting
4632 // since the item was actually found
4633 return status_ios;
4634 }
4635 }
4636 return status_osx;
b1ab9ed8
A
4637}
4638
4639OSStatus
4640SecItemCopyMatching_osx(
4641 CFDictionaryRef query,
4642 CFTypeRef *result)
4643{
4644 if (!query || !result)
427c49bc 4645 return errSecParam;
b1ab9ed8
A
4646 else
4647 *result = NULL;
4648
4649 CFAllocatorRef allocator = CFGetAllocator(query);
4650 CFIndex matchCount = 0;
4651 CFMutableArrayRef itemArray = NULL;
4652 SecKeychainItemRef item = NULL;
4653 SecIdentityRef identity = NULL;
427c49bc 4654 OSStatus tmpStatus, status = errSecSuccess;
b1ab9ed8
A
4655
4656 // validate input query parameters and create the search reference
4657 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
4658 require_action(itemParams != NULL, error_exit, itemParams = NULL);
4659
4660 // find the next match until we hit maxMatches, or no more matches found
4661 while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
427c49bc 4662 SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) {
b1ab9ed8
A
4663
4664 if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
4665 continue; // move on to next item
4666
4667 ++matchCount; // we have a match
4668
4669 tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result);
427c49bc 4670 if (tmpStatus && (status == errSecSuccess))
b1ab9ed8
A
4671 status = tmpStatus;
4672
4673 if (item) {
4674 CFRelease(item);
4675 item = NULL;
4676 }
4677 if (identity) {
4678 CFRelease(identity);
4679 identity = NULL;
4680 }
4681 }
4682
427c49bc 4683 if (status == errSecSuccess)
b1ab9ed8
A
4684 status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound;
4685
4686error_exit:
427c49bc 4687 if (status != errSecSuccess && result != NULL && *result != NULL) {
b1ab9ed8
A
4688 CFRelease(*result);
4689 *result = NULL;
4690 }
4691 _FreeSecItemParams(itemParams);
4692
4693 return status;
4694}
4695
4696OSStatus
4697SecItemCopyDisplayNames(
4698 CFArrayRef items,
4699 CFArrayRef *displayNames)
4700{
4701 BEGIN_SECAPI
4702 Required(items);
4703 Required(displayNames);
4704 //%%%TBI
427c49bc 4705 return errSecUnimplemented;
b1ab9ed8
A
4706 END_SECAPI
4707}
4708
4709OSStatus
4710SecItemAdd_osx(
4711 CFDictionaryRef attributes,
4712 CFTypeRef *result)
4713{
4714 if (!attributes)
427c49bc 4715 return errSecParam;
b1ab9ed8
A
4716 else if (result)
4717 *result = NULL;
4718
4719 CFAllocatorRef allocator = CFGetAllocator(attributes);
4720 CFMutableArrayRef itemArray = NULL;
4721 SecKeychainItemRef item = NULL;
427c49bc 4722 OSStatus tmpStatus, status = errSecSuccess;
b1ab9ed8
A
4723
4724 // validate input attribute parameters
4725 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
4726 require_action(itemParams != NULL, error_exit, itemParams = NULL);
4727
4728 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
4729 // since the private key should already be in a keychain by definition. We could support
4730 // this as a copy operation for the private key if a different keychain is specified,
4731 // but in any case it should try to add the certificate. See <rdar://8317887>.
4732 require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
4733
4734 if (!itemParams->useItems) {
4735 // create a single keychain item specified by the input attributes
4736 status = SecKeychainItemCreateFromContent(itemParams->itemClass,
4737 itemParams->attrList,
427c49bc 4738 (itemParams->itemData) ? (UInt32)CFDataGetLength(itemParams->itemData) : 0,
b1ab9ed8
A
4739 (itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL,
4740 itemParams->keychain,
4741 itemParams->access,
4742 &item);
4743 require_noerr(status, error_exit);
4744
4745 // return results (if requested)
4746 if (result) {
4747 itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1
4748 tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
427c49bc 4749 if (tmpStatus && (status == errSecSuccess))
b1ab9ed8
A
4750 status = tmpStatus;
4751 }
4752 CFRelease(item);
4753 }
4754 else {
4755 // add multiple items which are specified in the itemParams->useItems array.
4756 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
4757 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
4758 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
4759 //
427c49bc 4760 OSStatus aggregateStatus = errSecSuccess;
b1ab9ed8 4761 CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
427c49bc 4762 itemParams->maxMatches = (count > 1) ? (int)count : 2; // force results to always be returned as an array
b1ab9ed8
A
4763 for (ix=0; ix < count; ix++) {
4764 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
4765 if (anItem) {
4766 if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
4767 // SecCertificateRef item
4768 tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain);
4769 if (!tmpStatus && result) {
4770 tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result);
4771 }
4772 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4773 }
4774 else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) {
4775 // SecKeyRef item
4776 SecKeychainRef itemKeychain = NULL;
4777 tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
427c49bc 4778 if (tmpStatus == errSecSuccess) {
b1ab9ed8
A
4779 // key was in a keychain, so we can attempt to copy it
4780 SecKeychainItemRef itemCopy = NULL;
4781 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
4782 if (!tmpStatus && result) {
4783 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4784 }
4785 if (itemCopy) {
4786 CFRelease(itemCopy);
4787 }
4788 }
4789 else {
4790 // key was not in any keychain, so must be imported
4791 SecKeychainItemRef keyItem = NULL;
4792 tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem);
4793 if (!tmpStatus && result) {
4794 tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result);
4795 }
4796 if (keyItem) {
4797 CFRelease(keyItem);
4798 }
4799 }
4800 if (itemKeychain) {
4801 CFRelease(itemKeychain);
4802 }
4803 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4804 }
4805 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) {
4806 // SecKeychainItemRef item
4807 SecKeychainItemRef itemCopy = NULL;
4808 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
4809 if (!tmpStatus && result) {
4810 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4811 }
4812 if (itemCopy) {
4813 CFRelease(itemCopy);
4814 }
4815 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4816 }
4817 else if (CFDataGetTypeID() == CFGetTypeID(anItem)) {
4818 // CFDataRef item (persistent reference)
4819 SecKeychainItemRef realItem = NULL;
4820 tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
427c49bc 4821 if (tmpStatus == errSecSuccess) {
b1ab9ed8
A
4822 // persistent reference resolved to a keychain item, so we can attempt to copy it
4823 SecKeychainItemRef itemCopy = NULL;
4824 tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy);
4825 if (!tmpStatus && result) {
4826 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4827 }
4828 if (itemCopy) {
4829 CFRelease(itemCopy);
4830 }
4831 }
4832 if (realItem) {
4833 CFRelease(realItem);
4834 }
4835 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4836 }
4837 }
4838 } // end of itemList array loop
4839 status = aggregateStatus;
4840 } // end processing multiple items
4841
4842error_exit:
427c49bc 4843 if (status != errSecSuccess && result != NULL && *result != NULL) {
b1ab9ed8
A
4844 CFRelease(*result);
4845 *result = NULL;
4846 }
4847 _FreeSecItemParams(itemParams);
4848
4849 return status;
4850}
4851
4852OSStatus
4853SecItemUpdate_osx(
4854 CFDictionaryRef query,
4855 CFDictionaryRef attributesToUpdate)
4856{
4857 if (!query || !attributesToUpdate)
427c49bc 4858 return errSecParam;
b1ab9ed8
A
4859
4860 // run the provided query to get a list of items to update
4861 CFTypeRef results = NULL;
4862 OSStatus status = SecItemCopyMatching(query, &results);
427c49bc 4863 if (status != errSecSuccess)
b1ab9ed8
A
4864 return status; // nothing was matched, or the query was bad
4865
4866 CFArrayRef items = NULL;
4867 if (CFArrayGetTypeID() == CFGetTypeID(results)) {
4868 items = (CFArrayRef) results;
4869 }
4870 else {
4871 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
4872 CFRelease(results);
4873 }
4874
427c49bc 4875 OSStatus result = errSecSuccess;
b1ab9ed8
A
4876 CFIndex ix, count = CFArrayGetCount(items);
4877 for (ix=0; ix < count; ix++) {
4878 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
4879 if (anItem) {
4880 status = _UpdateKeychainItem(anItem, attributesToUpdate);
427c49bc 4881 result = _UpdateAggregateStatus(status, result, errSecSuccess);
b1ab9ed8
A
4882 }
4883 }
4884
4885 if (items) {
4886 CFRelease(items);
4887 }
4888 return result;
4889}
4890
4891OSStatus
4892SecItemDelete_osx(
4893 CFDictionaryRef query)
4894{
4895 if (!query)
427c49bc 4896 return errSecParam;
b1ab9ed8
A
4897
4898 // run the provided query to get a list of items to delete
4899 CFTypeRef results = NULL;
427c49bc
A
4900 OSStatus status = SecItemCopyMatching_osx(query, &results);
4901 if (status != errSecSuccess)
b1ab9ed8
A
4902 return status; // nothing was matched, or the query was bad
4903
4904 CFArrayRef items = NULL;
4905 if (CFArrayGetTypeID() == CFGetTypeID(results)) {
4906 items = (CFArrayRef) results;
4907 }
4908 else {
4909 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
4910 CFRelease(results);
4911 }
4912
427c49bc 4913 OSStatus result = errSecSuccess;
b1ab9ed8
A
4914 CFIndex ix, count = CFArrayGetCount(items);
4915 for (ix=0; ix < count; ix++) {
4916 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
4917 if (anItem) {
4918 if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) {
4919 status = _DeleteIdentity((SecIdentityRef)anItem);
4920 }
4921 else {
4922 status = _DeleteKeychainItem(anItem);
4923 }
427c49bc 4924 result = _UpdateAggregateStatus(status, result, errSecSuccess);
b1ab9ed8
A
4925 }
4926 }
4927
4928 if (items)
4929 CFRelease(items);
4930
4931 return result;
4932}