]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/Regressions/SOSTestDataSource.c
Security-57031.10.10.tar.gz
[apple/security.git] / Security / 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 <SecureObjectSync/SOSDataSource.h>
29 #include <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 struct SOSDigestVector removals;
54 struct SOSDigestVector additions;
55 SOSDataSourceNotifyBlock notifyBlock;
56 };
57
58 typedef struct SOSTestDataSourceFactory *SOSTestDataSourceFactoryRef;
59
60 struct SOSTestDataSourceFactory {
61 struct SOSDataSourceFactory dsf;
62 CFMutableDictionaryRef data_sources;
63 };
64
65
66 /* DataSource protocol. */
67 static 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;
71 CFDictionaryForEach(ds->d2database, ^(const void *key, const void *value) {
72 SOSDigestVectorAppend(&dv, CFDataGetBytePtr((CFDataRef)key));
73 });
74 SOSDigestVectorSort(&dv);
75 SOSManifestRef manifest = SOSManifestCreateWithDigestVector(&dv, error);
76 SOSDigestVectorFree(&dv);
77 ccdigest(ccsha1_di(), SOSManifestGetSize(manifest), SOSManifestGetBytePtr(manifest), ds->manifest_digest);
78 ds->clean = true;
79
80 return manifest;
81 }
82
83 static bool foreach_object(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error, void (^handle_object)(CFDataRef key, SOSObjectRef object, bool *stop)) {
84 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
85 ds->co_count++;
86 __block bool result = true;
87 SOSManifestForEach(manifest, ^(CFDataRef key, bool *stop) {
88 handle_object(key, (SOSObjectRef)CFDictionaryGetValue(ds->d2database, key), stop);
89 });
90 return result;
91 }
92
93 static bool dispose(SOSDataSourceRef data_source, CFErrorRef *error) {
94 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
95 CFReleaseSafe(ds->d2database);
96 CFReleaseSafe(ds->p2database);
97 CFReleaseSafe(ds->statedb);
98 free(ds);
99 return true;
100 }
101
102 static SOSObjectRef createWithPropertyList(CFDictionaryRef plist, CFErrorRef *error) {
103 return (SOSObjectRef)CFDictionaryCreateCopy(kCFAllocatorDefault, plist);
104 }
105
106 static 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);
116 (void)der_end;
117 } else if (error && *error == NULL) {
118 *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL);
119 }
120 return data;
121 }
122
123 static 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) {
130 *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL);
131 }
132 return digest;
133 }
134
135 static 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
148 static 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
190 static CFDictionaryRef copyPropertyList(SOSObjectRef object, CFErrorRef *error) {
191 return (CFDictionaryRef) CFRetain(object);
192 }
193
194 // Return the newest object
195 static 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
232 static 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 }
270
271 static CFStringRef dsGetName(SOSDataSourceRef ds) {
272 return CFSTR("The sky is made of butterflies");
273 }
274
275 static void dsSetNotifyPhaseBlock(SOSDataSourceRef ds, SOSDataSourceNotifyBlock notifyBlock) {
276 ((SOSTestDataSourceRef)ds)->notifyBlock = Block_copy(notifyBlock);
277 }
278
279 static 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
287 static 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
299 static 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
307 static 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
313 static CFDictionaryRef objectCopyBackup(SOSObjectRef object, uint64_t handle, CFErrorRef *error) {
314 // OMG We failed without an error.
315 assert(false);
316 return NULL;
317 }
318
319 SOSDataSourceRef 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);
344 ds->clean = false;
345
346 return (SOSDataSourceRef)ds;
347 }
348
349 static 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
359 static SOSDataSourceRef SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error)
360 {
361 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
362
363 return (SOSDataSourceRef) CFDictionaryGetValue(dsf->data_sources, dataSourceName);
364 }
365
366 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory)
367 {
368 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
369
370 CFReleaseNull(dsf->data_sources);
371 free(dsf);
372 }
373
374 SOSDataSourceFactoryRef 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
385 static bool do_nothing(SOSDataSourceRef ds, CFErrorRef *error) {
386 return true;
387 }
388
389 void SOSTestDataSourceFactoryAddDataSource(SOSDataSourceFactoryRef factory, CFStringRef name, SOSDataSourceRef ds)
390 {
391 SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory;
392
393 // TODO This hack sucks. It leaks now.
394 ds->dsRelease = do_nothing;
395
396 CFDictionarySetValue(dsf->data_sources, name, ds);
397
398 }
399
400 SOSMergeResult 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);
404 CFDataRef pk = copyPrimaryKey(object, error);
405 if (key && pk) {
406 SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk);
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)) {
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));
426 ds->clean = false;
427 }
428 CFRelease(merged);
429 }
430 }
431 CFReleaseSafe(pk);
432 CFReleaseSafe(key);
433 return result;
434 }
435
436 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source, CFDataRef key, CFErrorRef *error) {
437 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
438 return false;
439 }
440
441 CFMutableDictionaryRef SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source) {
442 struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
443 return ds->d2database;
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.
447 SOSObjectRef SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds, CFStringRef account, CFStringRef service, bool is_tomb, CFDataRef data) {
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;
470 SOSObjectRef object = ds->objectCreateWithPropertyList(dict, &localError);
471 if (!object) {
472 secerror("createWithPropertyList: %@ failed: %@", dict, localError);
473 CFRelease(localError);
474 }
475 CFRelease(dict);
476 return object;
477 }
478
479 SOSObjectRef SOSDataSourceCreateGenericItem(SOSDataSourceRef ds, CFStringRef account, CFStringRef service) {
480 return SOSDataSourceCreateGenericItemWithData(ds, account, service, false, NULL);
481 }
482
483 SOSObjectRef 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
503 exit:
504 CFReleaseNull(manifest);
505 CFReleaseNull(digest);
506 return result;
507 }