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