]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/Regressions/SOSTestDataSource.c
Security-57336.10.29.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.objectCopyPrimaryKey = copyPrimaryKey;
347 ds->ds.objectCreateWithPropertyList = createWithPropertyList;
348 ds->ds.objectCopyPropertyList = copyPropertyList;
349 ds->ds.objectCopyBackup = objectCopyBackup;
350
351 ds->d2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
352 ds->p2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
353 ds->statedb = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
354 ds->clean = false;
355 ds->changes = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
356
357 return (SOSDataSourceRef)ds;
358 }
359
360 static CFStringRef SOSTestDataSourceFactoryCopyName(SOSDataSourceFactoryRef factory)
361 {
362 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
363
364 __block CFStringRef result = NULL;
365 CFDictionaryForEach(dsf->data_sources, ^(const void*key, const void*value) { if (isString(key)) result = key; });
366
367 return CFRetainSafe(result);
368 }
369
370 static SOSDataSourceRef SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error)
371 {
372 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
373
374 return (SOSDataSourceRef) CFDictionaryGetValue(dsf->data_sources, dataSourceName);
375 }
376
377 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory)
378 {
379 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
380
381 CFReleaseNull(dsf->data_sources);
382 free(dsf);
383 }
384
385 SOSDataSourceFactoryRef SOSTestDataSourceFactoryCreate() {
386 SOSTestDataSourceFactoryRef dsf = calloc(1, sizeof(struct SOSTestDataSourceFactory));
387
388 dsf->dsf.copy_name = SOSTestDataSourceFactoryCopyName;
389 dsf->dsf.create_datasource = SOSTestDataSourceFactoryCreateDataSource;
390 dsf->dsf.release = SOSTestDataSourceFactoryDispose;
391 dsf->data_sources = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
392
393 return &(dsf->dsf);
394 }
395
396 static bool do_nothing(SOSDataSourceRef ds, CFErrorRef *error) {
397 return true;
398 }
399
400 void SOSTestDataSourceFactorySetDataSource(SOSDataSourceFactoryRef factory, CFStringRef name, SOSDataSourceRef ds)
401 {
402 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
403
404 // TODO This hack sucks. It leaks now.
405 ds->dsRelease = do_nothing;
406
407 CFDictionaryRemoveAllValues(dsf->data_sources);
408 CFDictionarySetValue(dsf->data_sources, name, ds);
409
410 }
411
412 SOSMergeResult SOSTestDataSourceAddObject(SOSDataSourceRef data_source, SOSObjectRef object, CFErrorRef *error) {
413 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
414 bool result = false;
415 CFDataRef key = copyDigest(object, error);
416 CFDataRef pk = copyPrimaryKey(object, error);
417 if (key && pk) {
418 SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk);
419 SOSObjectRef merged = NULL;
420 if (myObject) {
421 merged = copyMergedObject(object, myObject, error);
422 } else {
423 merged = object;
424 CFRetain(merged);
425 }
426 if (merged) {
427 result = true;
428 if (!CFEqualSafe(merged, myObject)) {
429 if (myObject) {
430 CFDataRef myKey = copyDigest(myObject, error);
431 CFDictionaryRemoveValue(ds->d2database, myKey);
432 CFReleaseSafe(myKey);
433 const void *values[2] = { myObject, merged };
434 CFTypeRef entry = CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
435 if (entry) {
436 CFArrayAppendValue(ds->changes, entry);
437 CFRelease(entry);
438 }
439 } else {
440 CFArrayAppendValue(ds->changes, merged);
441 }
442 CFDictionarySetValue(ds->d2database, key, merged);
443 CFDictionarySetValue(ds->p2database, pk, merged);
444 ds->clean = false;
445 }
446 CFRelease(merged);
447 }
448 }
449 CFReleaseSafe(pk);
450 CFReleaseSafe(key);
451 return result;
452 }
453
454 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source, CFDataRef key, CFErrorRef *error) {
455 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
456 return false;
457 }
458
459 CFMutableDictionaryRef SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source) {
460 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
461 return ds->d2database;
462 }
463
464 // This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now.
465 SOSObjectRef SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds, CFStringRef account, CFStringRef service, bool is_tomb, CFDataRef data) {
466 int32_t value = 0;
467 CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
468 value = 1;
469 CFNumberRef one = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
470 CFAbsoluteTime timestamp = 3700000 + (is_tomb ? 1 : 0);
471 CFDateRef now = CFDateCreate(kCFAllocatorDefault, timestamp);
472 CFDictionaryRef dict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
473 kSecClass, kSecClassGenericPassword,
474 kSecAttrSynchronizable, one,
475 kSecAttrTombstone, is_tomb ? one : zero,
476 kSecAttrAccount, account,
477 kSecAttrService, service,
478 kSecAttrCreationDate, now,
479 kSecAttrModificationDate, now,
480 kSecAttrAccessGroup, CFSTR("test"),
481 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
482 !is_tomb && data ? kSecValueData : NULL,data,
483 NULL);
484 CFRelease(one);
485 CFRelease(zero);
486 CFReleaseSafe(now);
487 CFErrorRef localError = NULL;
488 SOSObjectRef object = ds->objectCreateWithPropertyList(dict, &localError);
489 if (!object) {
490 secerror("createWithPropertyList: %@ failed: %@", dict, localError);
491 CFRelease(localError);
492 }
493 CFRelease(dict);
494 return object;
495 }
496
497 SOSObjectRef SOSDataSourceCreateGenericItem(SOSDataSourceRef ds, CFStringRef account, CFStringRef service) {
498 return SOSDataSourceCreateGenericItemWithData(ds, account, service, false, NULL);
499 }
500
501 SOSObjectRef SOSDataSourceCopyObject(SOSDataSourceRef ds, SOSObjectRef match, CFErrorRef *error)
502 {
503 __block SOSObjectRef result = NULL;
504
505 CFDataRef digest = SOSObjectCopyDigest(ds, match, error);
506 SOSManifestRef manifest = NULL;
507
508 require(digest, exit);
509 manifest = SOSManifestCreateWithData(digest, error);
510
511 SOSDataSourceForEachObject(ds, manifest, error, ^void (CFDataRef key, SOSObjectRef object, bool *stop) {
512 if (object == NULL) {
513 if (error && !*error) {
514 SecCFCreateErrorWithFormat(kSOSDataSourceObjectNotFoundError, sSOSDataSourceErrorDomain, NULL, error, 0, CFSTR("key %@ not in database"), key);
515 }
516 } else if (result == NULL) {
517 result = CFRetainSafe(object);
518 }
519 });
520
521 exit:
522 CFReleaseNull(manifest);
523 CFReleaseNull(digest);
524 return result;
525 }