]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/Regressions/SOSTestDataSource.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / Regressions / SOSTestDataSource.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include "SOSTestDataSource.h"
26
27 #include <corecrypto/ccder.h>
28 #include <Security/SecureObjectSync/SOSDataSource.h>
29 #include <Security/SecureObjectSync/SOSDigestVector.h>
30 #include <Security/SecureObjectSync/SOSViews.h>
31
32 #include <utilities/array_size.h>
33 #include <utilities/der_plist.h>
34 #include <utilities/SecCFError.h>
35 #include <utilities/SecCFWrappers.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecItemPriv.h>
38 #include <AssertMacros.h>
39
40 CFStringRef sSOSDataSourceErrorDomain = CFSTR("com.apple.datasource");
41
42 typedef struct SOSTestDataSource *SOSTestDataSourceRef;
43
44 struct SOSTestDataSource {
45 struct SOSDataSource ds;
46 unsigned gm_count;
47 unsigned cm_count;
48 unsigned co_count;
49 CFMutableDictionaryRef d2database;
50 CFMutableDictionaryRef p2database;
51 CFMutableDictionaryRef statedb;
52 uint8_t manifest_digest[SOSDigestSize];
53 bool clean;
54
55 CFMutableArrayRef changes;
56 SOSDataSourceNotifyBlock notifyBlock;
57 };
58
59 typedef struct SOSTestDataSourceFactory *SOSTestDataSourceFactoryRef;
60
61 struct SOSTestDataSourceFactory {
62 struct SOSDataSourceFactory dsf;
63 CFMutableDictionaryRef data_sources;
64 };
65
66
67 /* DataSource protocol. */
68 static SOSManifestRef dsCopyManifestWithViewNameSet(SOSDataSourceRef data_source, CFSetRef viewNameSet, CFErrorRef *error) {
69 if (!CFSetContainsValue(viewNameSet, kSOSViewKeychainV0))
70 return SOSManifestCreateWithData(NULL, error);
71
72 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
73 ds->cm_count++;
74 __block struct SOSDigestVector dv = SOSDigestVectorInit;
75 CFDictionaryForEach(ds->d2database, ^(const void *key, const void *value) {
76 SOSDigestVectorAppend(&dv, CFDataGetBytePtr((CFDataRef)key));
77 });
78 SOSDigestVectorSort(&dv);
79 SOSManifestRef manifest = SOSManifestCreateWithDigestVector(&dv, error);
80 SOSDigestVectorFree(&dv);
81 ccdigest(ccsha1_di(), SOSManifestGetSize(manifest), SOSManifestGetBytePtr(manifest), ds->manifest_digest);
82 ds->clean = true;
83
84 return manifest;
85 }
86
87 static bool foreach_object(SOSDataSourceRef data_source, SOSTransactionRef txn, SOSManifestRef manifest, CFErrorRef *error, void (^handle_object)(CFDataRef key, SOSObjectRef object, bool *stop)) {
88 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
89 ds->co_count++;
90 __block bool result = true;
91 SOSManifestForEach(manifest, ^(CFDataRef key, bool *stop) {
92 handle_object(key, (SOSObjectRef)CFDictionaryGetValue(ds->d2database, key), stop);
93 });
94 return result;
95 }
96
97 static bool dispose(SOSDataSourceRef data_source, CFErrorRef *error) {
98 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
99 CFReleaseSafe(ds->d2database);
100 CFReleaseSafe(ds->p2database);
101 CFReleaseSafe(ds->statedb);
102 CFReleaseSafe(ds->changes);
103 free(ds);
104 return true;
105 }
106
107 static SOSObjectRef createWithPropertyList(CFDictionaryRef plist, CFErrorRef *error) {
108 return (SOSObjectRef)CFDictionaryCreateCopy(kCFAllocatorDefault, plist);
109 }
110
111 static CFDataRef SOSObjectCopyDER(SOSObjectRef object, CFErrorRef *error) {
112 CFDictionaryRef dict = (CFDictionaryRef)object;
113 size_t size = der_sizeof_plist(dict, error);
114 CFMutableDataRef data = CFDataCreateMutable(0, size);
115 if (data) {
116 CFDataSetLength(data, size);
117 uint8_t *der = (uint8_t *)CFDataGetMutableBytePtr(data);
118 uint8_t *der_end = der + size;
119 der_end = der_encode_plist(dict, error, der, der_end);
120 assert(der_end == der);
121 (void)der_end;
122 } else if (error && *error == NULL) {
123 *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL);
124 }
125 return data;
126 }
127
128 static CFDataRef ccdigest_copy_data(const struct ccdigest_info *di, size_t len,
129 const void *data, CFErrorRef *error) {
130 CFMutableDataRef digest = CFDataCreateMutable(0, di->output_size);
131 if (digest) {
132 CFDataSetLength(digest, di->output_size);
133 ccdigest(di, len, data, CFDataGetMutableBytePtr(digest));
134 } else if (error && *error == NULL) {
135 *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL);
136 }
137 return digest;
138 }
139
140 static CFDataRef copyDigest(SOSObjectRef object, CFErrorRef *error) {
141 CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)object);
142 CFDictionaryRemoveValue(ocopy, kSecClass);
143 CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error);
144 CFRelease(ocopy);
145 CFDataRef digest = NULL;
146 if (der) {
147 digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error);
148 CFRelease(der);
149 }
150 return digest;
151 }
152
153 static CFDataRef copyPrimaryKey(SOSObjectRef object, CFErrorRef *error) {
154 CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
155 CFTypeRef pkNames[] = {
156 CFSTR("acct"),
157 CFSTR("agrp"),
158 CFSTR("svce"),
159 CFSTR("sync"),
160 CFSTR("sdmn"),
161 CFSTR("srvr"),
162 CFSTR("ptcl"),
163 CFSTR("atyp"),
164 CFSTR("port"),
165 CFSTR("path"),
166 CFSTR("ctyp"),
167 CFSTR("issr"),
168 CFSTR("slnr"),
169 CFSTR("kcls"),
170 CFSTR("klbl"),
171 CFSTR("atag"),
172 CFSTR("crtr"),
173 CFSTR("type"),
174 CFSTR("bsiz"),
175 CFSTR("esiz"),
176 CFSTR("sdat"),
177 CFSTR("edat"),
178 };
179 CFSetRef pkAttrs = CFSetCreate(kCFAllocatorDefault, pkNames, array_size(pkNames), &kCFTypeSetCallBacks);
180 CFDictionaryForEach((CFDictionaryRef)object, ^(const void *key, const void *value) {
181 if (CFSetContainsValue(pkAttrs, key))
182 CFDictionaryAddValue(ocopy, key, value);
183 });
184 CFRelease(pkAttrs);
185 CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error);
186 CFRelease(ocopy);
187 CFDataRef digest = NULL;
188 if (der) {
189 digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error);
190 CFRelease(der);
191 }
192 return digest;
193 }
194
195 static CFDictionaryRef copyPropertyList(SOSObjectRef object, CFErrorRef *error) {
196 return (CFDictionaryRef) CFRetain(object);
197 }
198
199 // Return the newest object
200 static SOSObjectRef copyMergedObject(SOSObjectRef object1, SOSObjectRef object2, CFErrorRef *error) {
201 CFDictionaryRef dict1 = (CFDictionaryRef)object1;
202 CFDictionaryRef dict2 = (CFDictionaryRef)object2;
203 SOSObjectRef result = NULL;
204 CFDateRef m1, m2;
205 m1 = CFDictionaryGetValue(dict1, kSecAttrModificationDate);
206 m2 = CFDictionaryGetValue(dict2, kSecAttrModificationDate);
207 switch (CFDateCompare(m1, m2, NULL)) {
208 case kCFCompareGreaterThan:
209 result = (SOSObjectRef)dict1;
210 break;
211 case kCFCompareLessThan:
212 result = (SOSObjectRef)dict2;
213 break;
214 case kCFCompareEqualTo:
215 {
216 // Return the item with the smallest digest.
217 CFDataRef digest1 = copyDigest(object1, error);
218 CFDataRef digest2 = copyDigest(object2, error);
219 if (digest1 && digest2) switch (CFDataCompare(digest1, digest2)) {
220 case kCFCompareGreaterThan:
221 case kCFCompareEqualTo:
222 result = (SOSObjectRef)dict2;
223 break;
224 case kCFCompareLessThan:
225 result = (SOSObjectRef)dict1;
226 break;
227 }
228 CFReleaseSafe(digest2);
229 CFReleaseSafe(digest1);
230 break;
231 }
232 }
233 CFRetainSafe(result);
234 return result;
235 }
236
237 static SOSMergeResult mergeObject(SOSTransactionRef txn, SOSObjectRef object, SOSObjectRef *mergedObject, CFErrorRef *error) {
238 SOSTestDataSourceRef ds = (SOSTestDataSourceRef)txn;
239 SOSMergeResult mr = kSOSMergeFailure;
240 CFDataRef pk = copyPrimaryKey(object, error);
241 if (!pk) return mr;
242 SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk);
243 if (myObject) {
244 SOSObjectRef merged = copyMergedObject(object, myObject, error);
245 if (mergedObject) *mergedObject = CFRetainSafe(merged);
246 if (CFEqualSafe(merged, myObject)) {
247 mr = kSOSMergeLocalObject;
248 } else if (CFEqualSafe(merged, object)) {
249 mr = kSOSMergePeersObject;
250 } else {
251 mr = kSOSMergeCreatedObject;
252 }
253 if (mr != kSOSMergeLocalObject) {
254 CFDataRef myKey = copyDigest(myObject, error);
255 CFDictionaryRemoveValue(ds->d2database, myKey);
256 CFReleaseSafe(myKey);
257 CFDataRef key = copyDigest(merged, error);
258 CFDictionarySetValue(ds->d2database, key, merged);
259 const void *values[2] = { myObject, merged };
260 CFTypeRef entry = CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
261 if (entry) {
262 CFArrayAppendValue(ds->changes, entry);
263 CFRelease(entry);
264 }
265 CFReleaseSafe(key);
266 CFDictionarySetValue(ds->p2database, pk, merged);
267 }
268 CFReleaseSafe(merged);
269 } else {
270 SOSTestDataSourceAddObject((SOSDataSourceRef)ds, object, error);
271 mr = kSOSMergePeersObject;
272 }
273 CFReleaseSafe(pk);
274 return mr;
275 }
276
277 static CFStringRef dsGetName(SOSDataSourceRef ds) {
278 return CFSTR("The sky is made of butterflies");
279 }
280
281 static void dsAddNotifyPhaseBlock(SOSDataSourceRef ds, SOSDataSourceNotifyBlock notifyBlock) {
282 SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds;
283 assert(tds->notifyBlock == NULL);
284 tds->notifyBlock = Block_copy(notifyBlock);
285 }
286
287 static CFDataRef dsCopyStateWithKey(SOSDataSourceRef ds, CFStringRef key, CFStringRef pdmn, SOSTransactionRef txn, CFErrorRef *error) {
288 SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds;
289 CFStringRef dbkey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-%@"), pdmn, key);
290 CFDataRef state = CFDictionaryGetValue(tds->statedb, dbkey);
291 CFReleaseSafe(dbkey);
292 return CFRetainSafe(state);
293 }
294
295 static CFDataRef dsCopyItemDataWithKeys(SOSDataSourceRef data_source, CFDictionaryRef keys, CFErrorRef *error) {
296 SecError(errSecUnimplemented, error, CFSTR("dsCopyItemDataWithKeys on test data source not implemented"));
297 return NULL;
298 }
299
300 static bool dsWith(SOSDataSourceRef ds, CFErrorRef *error, SOSDataSourceTransactionSource source, bool onCommitQueue, void(^transaction)(SOSTransactionRef txn, bool *commit)) {
301 SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds;
302 bool commit = true;
303 transaction((SOSTransactionRef)ds, &commit);
304 if (commit && ((SOSTestDataSourceRef)ds)->notifyBlock && (CFArrayGetCount(tds->changes))) {
305 ((SOSTestDataSourceRef)ds)->notifyBlock(ds, (SOSTransactionRef)ds, kSOSDataSourceTransactionWillCommit, source, tds->changes);
306 CFArrayRemoveAllValues(tds->changes);
307 }
308 return true;
309 }
310
311 static bool dsReadWith(SOSDataSourceRef ds, CFErrorRef *error, SOSDataSourceTransactionSource source, void(^perform)(SOSTransactionRef txn)) {
312 SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds;
313 perform((SOSTransactionRef)tds);
314 return true;
315 }
316
317 static bool dsSetStateWithKey(SOSDataSourceRef ds, SOSTransactionRef txn, CFStringRef pdmn, CFStringRef key, CFDataRef state, CFErrorRef *error) {
318 SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds;
319 CFStringRef dbkey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-%@"), pdmn, key);
320 CFDictionarySetValue(tds->statedb, dbkey, state);
321 CFReleaseSafe(dbkey);
322 return true;
323 }
324
325 static bool dsRestoreObject(SOSTransactionRef txn, uint64_t handle, CFDictionaryRef item, CFErrorRef *error) {
326 // TODO: Just call merge, probably doesn't belong in protocol at all
327 assert(false);
328 return true;
329 }
330
331 static CFDictionaryRef objectCopyBackup(SOSObjectRef object, uint64_t handle, CFErrorRef *error) {
332 // OMG We failed without an error.
333 assert(false);
334 return NULL;
335 }
336
337 SOSDataSourceRef SOSTestDataSourceCreate(void) {
338 SOSTestDataSourceRef ds = calloc(1, sizeof(struct SOSTestDataSource));
339
340 ds->ds.engine = NULL;
341 ds->ds.dsGetName = dsGetName;
342 ds->ds.dsAddNotifyPhaseBlock = dsAddNotifyPhaseBlock;
343 ds->ds.dsCopyManifestWithViewNameSet = dsCopyManifestWithViewNameSet;
344 ds->ds.dsForEachObject = foreach_object;
345 ds->ds.dsCopyStateWithKey = dsCopyStateWithKey;
346 ds->ds.dsCopyItemDataWithKeys = dsCopyItemDataWithKeys;
347
348 ds->ds.dsWith = dsWith;
349 ds->ds.dsRelease = dispose;
350 ds->ds.dsReadWith = dsReadWith;
351
352 ds->ds.dsMergeObject = mergeObject;
353 ds->ds.dsSetStateWithKey = dsSetStateWithKey;
354 ds->ds.dsRestoreObject = dsRestoreObject;
355
356 ds->ds.objectCopyDigest = copyDigest;
357 ds->ds.objectCreateWithPropertyList = createWithPropertyList;
358 ds->ds.objectCopyPropertyList = copyPropertyList;
359 ds->ds.objectCopyBackup = objectCopyBackup;
360
361 ds->d2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
362 ds->p2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
363 ds->statedb = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
364 ds->clean = false;
365 ds->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
366
367 return (SOSDataSourceRef)ds;
368 }
369
370 static CFStringRef SOSTestDataSourceFactoryCopyName(SOSDataSourceFactoryRef factory)
371 {
372 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
373
374 __block CFStringRef result = NULL;
375 CFDictionaryForEach(dsf->data_sources, ^(const void*key, const void*value) { if (isString(key)) result = key; });
376
377 return CFRetainSafe(result);
378 }
379
380 static SOSDataSourceRef SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error)
381 {
382 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
383
384 return (SOSDataSourceRef) CFDictionaryGetValue(dsf->data_sources, dataSourceName);
385 }
386
387 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory)
388 {
389 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
390
391 CFReleaseNull(dsf->data_sources);
392 free(dsf);
393 }
394
395 SOSDataSourceFactoryRef SOSTestDataSourceFactoryCreate() {
396 SOSTestDataSourceFactoryRef dsf = calloc(1, sizeof(struct SOSTestDataSourceFactory));
397
398 dsf->dsf.copy_name = SOSTestDataSourceFactoryCopyName;
399 dsf->dsf.create_datasource = SOSTestDataSourceFactoryCreateDataSource;
400 dsf->dsf.release = SOSTestDataSourceFactoryDispose;
401 dsf->data_sources = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
402
403 return &(dsf->dsf);
404 }
405
406 static bool do_nothing(SOSDataSourceRef ds, CFErrorRef *error) {
407 return true;
408 }
409
410 void SOSTestDataSourceFactorySetDataSource(SOSDataSourceFactoryRef factory, CFStringRef name, SOSDataSourceRef ds)
411 {
412 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
413
414 // TODO This hack sucks. It leaks now.
415 ds->dsRelease = do_nothing;
416
417 CFDictionaryRemoveAllValues(dsf->data_sources);
418 CFDictionarySetValue(dsf->data_sources, name, ds);
419
420 }
421
422 SOSMergeResult SOSTestDataSourceAddObject(SOSDataSourceRef data_source, SOSObjectRef object, CFErrorRef *error) {
423 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
424 bool result = false;
425 CFDataRef key = copyDigest(object, error);
426 CFDataRef pk = copyPrimaryKey(object, error);
427 if (key && pk) {
428 SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk);
429 SOSObjectRef merged = NULL;
430 if (myObject) {
431 merged = copyMergedObject(object, myObject, error);
432 } else {
433 merged = object;
434 CFRetain(merged);
435 }
436 if (merged) {
437 result = true;
438 if (!CFEqualSafe(merged, myObject)) {
439 if (myObject) {
440 CFDataRef myKey = copyDigest(myObject, error);
441 CFDictionaryRemoveValue(ds->d2database, myKey);
442 CFReleaseSafe(myKey);
443 const void *values[2] = { myObject, merged };
444 CFTypeRef entry = CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
445 if (entry) {
446 CFArrayAppendValue(ds->changes, entry);
447 CFRelease(entry);
448 }
449 } else {
450 CFArrayAppendValue(ds->changes, merged);
451 }
452 CFDictionarySetValue(ds->d2database, key, merged);
453 CFDictionarySetValue(ds->p2database, pk, merged);
454 ds->clean = false;
455 }
456 CFRelease(merged);
457 }
458 }
459 CFReleaseSafe(pk);
460 CFReleaseSafe(key);
461 return result;
462 }
463
464 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source, CFDataRef key, CFErrorRef *error) {
465 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
466 return false;
467 }
468
469 CFMutableDictionaryRef SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source) {
470 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
471 return ds->d2database;
472 }
473
474 // This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now.
475 SOSObjectRef SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds, CFStringRef account, CFStringRef service, bool is_tomb, CFDataRef data) {
476 int32_t value = 0;
477 CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
478 value = 1;
479 CFNumberRef one = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
480 CFAbsoluteTime timestamp = 3700000 + (is_tomb ? 1 : 0);
481 CFDateRef now = CFDateCreate(kCFAllocatorDefault, timestamp);
482 CFDictionaryRef dict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
483 kSecClass, kSecClassGenericPassword,
484 kSecAttrSynchronizable, one,
485 kSecAttrTombstone, is_tomb ? one : zero,
486 kSecAttrAccount, account,
487 kSecAttrService, service,
488 kSecAttrCreationDate, now,
489 kSecAttrModificationDate, now,
490 kSecAttrAccessGroup, CFSTR("test"),
491 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
492 !is_tomb && data ? kSecValueData : NULL,data,
493 NULL);
494 CFRelease(one);
495 CFRelease(zero);
496 CFReleaseSafe(now);
497 CFErrorRef localError = NULL;
498 SOSObjectRef object = ds->objectCreateWithPropertyList(dict, &localError);
499 if (!object) {
500 secerror("createWithPropertyList: %@ failed: %@", dict, localError);
501 CFRelease(localError);
502 }
503 CFRelease(dict);
504 return object;
505 }
506
507 SOSObjectRef SOSDataSourceCreateGenericItem(SOSDataSourceRef ds, CFStringRef account, CFStringRef service) {
508 return SOSDataSourceCreateGenericItemWithData(ds, account, service, false, NULL);
509 }
510
511 SOSObjectRef SOSDataSourceCopyObject(SOSDataSourceRef ds, SOSObjectRef match, CFErrorRef *error)
512 {
513 __block SOSObjectRef result = NULL;
514
515 CFDataRef digest = SOSObjectCopyDigest(ds, match, error);
516 SOSManifestRef manifest = NULL;
517
518 require(digest, exit);
519 manifest = SOSManifestCreateWithData(digest, error);
520
521 SOSDataSourceForEachObject(ds, NULL, manifest, error, ^void (CFDataRef key, SOSObjectRef object, bool *stop) {
522 if (object == NULL) {
523 if (error && !*error) {
524 SecCFCreateErrorWithFormat(kSOSDataSourceObjectNotFoundError, sSOSDataSourceErrorDomain, NULL, error, 0, CFSTR("key %@ not in database"), key);
525 }
526 } else if (result == NULL) {
527 result = CFRetainSafe(object);
528 }
529 });
530
531 exit:
532 CFReleaseNull(manifest);
533 CFReleaseNull(digest);
534 return result;
535 }