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