X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/c38e3ce98599a410a47dc10253faa4d5830f13b2..427c49bcad63d042b29ada2ac27e3dfc4845c779:/sec/SOSCircle/Regressions/SOSTestDataSource.c diff --git a/sec/SOSCircle/Regressions/SOSTestDataSource.c b/sec/SOSCircle/Regressions/SOSTestDataSource.c new file mode 100644 index 00000000..1045f804 --- /dev/null +++ b/sec/SOSCircle/Regressions/SOSTestDataSource.c @@ -0,0 +1,382 @@ +// +// SOSTestDataSource.c +// sec +// +// Created by Michael Brouwer on 9/28/12. +// +// + +#include "SOSTestDataSource.h" + +#include +#include +#include +#include +#include +#include +#include + +static CFStringRef sErrorDomain = CFSTR("com.apple.testdatasource"); + +enum { + kSOSObjectMallocFailed = 1, + kAddDuplicateEntry, + kSOSObjectNotFouncError = 1, +}; + +typedef struct SOSTestDataSource *SOSTestDataSourceRef; + +struct SOSTestDataSource { + struct SOSDataSource ds; + unsigned gm_count; + unsigned cm_count; + unsigned co_count; + CFMutableDictionaryRef database; + uint8_t manifest_digest[SOSDigestSize]; + bool clean; +}; + +typedef struct SOSTestDataSourceFactory *SOSTestDataSourceFactoryRef; + +struct SOSTestDataSourceFactory { + struct SOSDataSourceFactory dsf; + CFMutableDictionaryRef data_sources; +}; + + +/* DataSource protocol. */ +static bool get_manifest_digest(SOSDataSourceRef data_source, uint8_t *out_digest, CFErrorRef *error) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + if (!ds->clean) { + SOSManifestRef mf = data_source->copy_manifest(data_source, error); + if (mf) { + CFRelease(mf); + } else { + return false; + } + } + memcpy(out_digest, ds->manifest_digest, SOSDigestSize); + ds->gm_count++; + return true; +} + +static SOSManifestRef copy_manifest(SOSDataSourceRef data_source, CFErrorRef *error) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + ds->cm_count++; + __block struct SOSDigestVector dv = SOSDigestVectorInit; + CFDictionaryForEach(ds->database, ^(const void *key, const void *value) { + SOSDigestVectorAppend(&dv, CFDataGetBytePtr((CFDataRef)key)); + }); + SOSDigestVectorSort(&dv); + SOSManifestRef manifest = SOSManifestCreateWithBytes((const uint8_t *)dv.digest, dv.count * SOSDigestSize, error); + SOSDigestVectorFree(&dv); + ccdigest(ccsha1_di(), SOSManifestGetSize(manifest), SOSManifestGetBytePtr(manifest), ds->manifest_digest); + ds->clean = true; + + return manifest; +} + +static bool foreach_object(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error, bool (^handle_object)(SOSObjectRef object, CFErrorRef *error)) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + ds->co_count++; + __block bool result = true; + SOSManifestForEach(manifest, ^(CFDataRef key) { + CFDictionaryRef dict = (CFDictionaryRef)CFDictionaryGetValue(ds->database, key); + if (dict) { + result = result && handle_object((SOSObjectRef)dict, error); + } else { + result = false; + if (error) { + // TODO: Collect all missing keys in an array and return an single error at the end with all collected keys + // Collect all errors as chained errors. + CFErrorRef old_error = *error; + *error = NULL; + SecCFCreateErrorWithFormat(kSOSObjectNotFouncError, sErrorDomain, old_error, error, 0, CFSTR("key %@ not in database"), key); + } + } + }); + return result; +} + +static void dispose(SOSDataSourceRef data_source) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + free(ds); +} + +static SOSObjectRef createWithPropertyList(SOSDataSourceRef ds, CFDictionaryRef plist, CFErrorRef *error) { + return (SOSObjectRef)CFDictionaryCreateCopy(kCFAllocatorDefault, plist); +} + +static CFDataRef SOSObjectCopyDER(SOSObjectRef object, CFErrorRef *error) { + CFDictionaryRef dict = (CFDictionaryRef)object; + size_t size = der_sizeof_plist(dict, error); + CFMutableDataRef data = CFDataCreateMutable(0, size); + if (data) { + CFDataSetLength(data, size); + uint8_t *der = (uint8_t *)CFDataGetMutableBytePtr(data); + uint8_t *der_end = der + size; + der_end = der_encode_plist(dict, error, der, der_end); + assert(der_end == der); + } else if (error && *error == NULL) { + *error = CFErrorCreate(0, sErrorDomain, kSOSObjectMallocFailed, NULL); + } + return data; +} + +static CFDataRef ccdigest_copy_data(const struct ccdigest_info *di, size_t len, + const void *data, CFErrorRef *error) { + CFMutableDataRef digest = CFDataCreateMutable(0, di->output_size); + if (digest) { + CFDataSetLength(digest, di->output_size); + ccdigest(di, len, data, CFDataGetMutableBytePtr(digest)); + } else if (error && *error == NULL) { + *error = CFErrorCreate(0, sErrorDomain, kSOSObjectMallocFailed, NULL); + } + return digest; +} + +static CFDataRef copyDigest(SOSObjectRef object, CFErrorRef *error) { + CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)object); + CFDictionaryRemoveValue(ocopy, kSecClass); + CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error); + CFRelease(ocopy); + CFDataRef digest = NULL; + if (der) { + digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error); + CFRelease(der); + } + return digest; +} + +static CFDataRef copyPrimaryKey(SOSObjectRef object, CFErrorRef *error) { + CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); + CFTypeRef pkNames[] = { + CFSTR("acct"), + CFSTR("agrp"), + CFSTR("svce"), + CFSTR("sync"), + CFSTR("sdmn"), + CFSTR("srvr"), + CFSTR("ptcl"), + CFSTR("atyp"), + CFSTR("port"), + CFSTR("path"), + CFSTR("ctyp"), + CFSTR("issr"), + CFSTR("slnr"), + CFSTR("kcls"), + CFSTR("klbl"), + CFSTR("atag"), + CFSTR("crtr"), + CFSTR("type"), + CFSTR("bsiz"), + CFSTR("esiz"), + CFSTR("sdat"), + CFSTR("edat"), + }; + CFSetRef pkAttrs = CFSetCreate(kCFAllocatorDefault, pkNames, array_size(pkNames), &kCFTypeSetCallBacks); + CFDictionaryForEach((CFDictionaryRef)object, ^(const void *key, const void *value) { + if (CFSetContainsValue(pkAttrs, key)) + CFDictionaryAddValue(ocopy, key, value); + }); + CFRelease(pkAttrs); + CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error); + CFRelease(ocopy); + CFDataRef digest = NULL; + if (der) { + digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error); + CFRelease(der); + } + return digest; +} + +static CFDictionaryRef copyPropertyList(SOSObjectRef object, CFErrorRef *error) { + return (CFDictionaryRef) CFRetain(object); +} + +// Return the newest object +static SOSObjectRef copyMergedObject(SOSObjectRef object1, SOSObjectRef object2, CFErrorRef *error) { + CFDictionaryRef dict1 = (CFDictionaryRef)object1; + CFDictionaryRef dict2 = (CFDictionaryRef)object2; + SOSObjectRef result = NULL; + CFDateRef m1, m2; + m1 = CFDictionaryGetValue(dict1, kSecAttrModificationDate); + m2 = CFDictionaryGetValue(dict2, kSecAttrModificationDate); + switch (CFDateCompare(m1, m2, NULL)) { + case kCFCompareGreaterThan: + result = (SOSObjectRef)dict1; + break; + case kCFCompareLessThan: + result = (SOSObjectRef)dict2; + break; + case kCFCompareEqualTo: + { + // Return the item with the smallest digest. + CFDataRef digest1 = copyDigest(object1, error); + CFDataRef digest2 = copyDigest(object2, error); + if (digest1 && digest2) switch (CFDataCompare(digest1, digest2)) { + case kCFCompareGreaterThan: + case kCFCompareEqualTo: + result = (SOSObjectRef)dict2; + break; + case kCFCompareLessThan: + result = (SOSObjectRef)dict1; + break; + } + CFReleaseSafe(digest2); + CFReleaseSafe(digest1); + break; + } + } + CFRetainSafe(result); + return result; +} + +SOSDataSourceRef SOSTestDataSourceCreate(void) { + SOSTestDataSourceRef ds = calloc(1, sizeof(struct SOSTestDataSource)); + + ds->ds.get_manifest_digest = get_manifest_digest; + ds->ds.copy_manifest = copy_manifest; + ds->ds.foreach_object = foreach_object; + ds->ds.release = dispose; + ds->ds.add = SOSTestDataSourceAddObject; + + ds->ds.createWithPropertyList = createWithPropertyList; + ds->ds.copyDigest = copyDigest; + ds->ds.copyPrimaryKey = copyPrimaryKey; + ds->ds.copyPropertyList = copyPropertyList; + ds->ds.copyMergedObject = copyMergedObject; + + ds->database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + ds->clean = false; + + return (SOSDataSourceRef)ds; +} + +static CFArrayRef SOSTestDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory) +{ + SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; + CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); + + CFDictionaryForEach(dsf->data_sources, ^(const void*key, const void*value) { CFArrayAppendValue(result, key); }); + + return result; +} + +static SOSDataSourceRef SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, bool readOnly __unused, CFErrorRef *error) +{ + SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; + + return (SOSDataSourceRef) CFDictionaryGetValue(dsf->data_sources, dataSourceName); +} + +static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory) +{ + SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; + + CFReleaseNull(dsf->data_sources); + free(dsf); +} + +SOSDataSourceFactoryRef SOSTestDataSourceFactoryCreate() { + SOSTestDataSourceFactoryRef dsf = calloc(1, sizeof(struct SOSTestDataSourceFactory)); + + dsf->dsf.copy_names = SOSTestDataSourceFactoryCopyNames; + dsf->dsf.create_datasource = SOSTestDataSourceFactoryCreateDataSource; + dsf->dsf.release = SOSTestDataSourceFactoryDispose; + dsf->data_sources = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + + return &(dsf->dsf); +} + +static void do_nothing(SOSDataSourceRef ds) +{ +} + +void SOSTestDataSourceFactoryAddDataSource(SOSDataSourceFactoryRef factory, CFStringRef name, SOSDataSourceRef ds) +{ + SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; + + // TODO This hack sucks. It leaks now. + ds->release = do_nothing; + + CFDictionarySetValue(dsf->data_sources, name, ds); + +} + +SOSMergeResult SOSTestDataSourceAddObject(SOSDataSourceRef data_source, SOSObjectRef object, CFErrorRef *error) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + bool result = false; + CFDataRef key = copyDigest(object, error); + if (key) { + SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->database, key); + SOSObjectRef merged = NULL; + if (myObject) { + merged = copyMergedObject(object, myObject, error); + } else { + merged = object; + CFRetain(merged); + } + if (merged) { + result = true; + if (!CFEqualSafe(merged, myObject)) { + CFDictionarySetValue(ds->database, key, merged); + ds->clean = false; + } + CFRelease(merged); + } + CFRelease(key); + } + return result; +} + +bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source, CFDataRef key, CFErrorRef *error) { + //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + return false; +} + +CFMutableDictionaryRef SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source) { + struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; + return ds->database; +} + +// This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now. +SOSObjectRef SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds, CFStringRef account, CFStringRef service, bool is_tomb, CFDataRef data) { +#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) + abort(); +#else + int32_t value = 0; + CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + value = 1; + CFNumberRef one = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + CFAbsoluteTime timestamp = 3700000; + CFDateRef now = CFDateCreate(kCFAllocatorDefault, timestamp); + CFDictionaryRef dict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, + kSecClass, kSecClassGenericPassword, + kSecAttrSynchronizable, one, + kSecAttrTombstone, is_tomb ? one : zero, + kSecAttrAccount, account, + kSecAttrService, service, + kSecAttrCreationDate, now, + kSecAttrModificationDate, now, + kSecAttrAccessGroup, CFSTR("test"), + kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, + !is_tomb && data ? kSecValueData : NULL,data, + NULL); + CFRelease(one); + CFRelease(zero); + CFReleaseSafe(now); + CFErrorRef localError = NULL; + SOSObjectRef object = ds->createWithPropertyList(ds, dict, &localError); + if (!object) { + secerror("createWithPropertyList: %@ failed: %@", dict, localError); + CFRelease(localError); + } + CFRelease(dict); + return object; +#endif +} + +SOSObjectRef SOSDataSourceCreateGenericItem(SOSDataSourceRef ds, CFStringRef account, CFStringRef service) { + return SOSDataSourceCreateGenericItemWithData(ds, account, service, false, NULL); +}