]> git.saurik.com Git - apple/security.git/blobdiff - sec/SOSCircle/Regressions/SOSTestDataSource.c
Security-55471.tar.gz
[apple/security.git] / sec / SOSCircle / Regressions / SOSTestDataSource.c
diff --git a/sec/SOSCircle/Regressions/SOSTestDataSource.c b/sec/SOSCircle/Regressions/SOSTestDataSource.c
new file mode 100644 (file)
index 0000000..1045f80
--- /dev/null
@@ -0,0 +1,382 @@
+//
+//  SOSTestDataSource.c
+//  sec
+//
+//  Created by Michael Brouwer on 9/28/12.
+//
+//
+
+#include "SOSTestDataSource.h"
+
+#include <corecrypto/ccder.h>
+#include <SecureObjectSync/SOSEngine.h>
+#include <utilities/array_size.h>
+#include <utilities/der_plist.h>
+#include <utilities/SecCFError.h>
+#include <utilities/SecCFWrappers.h>
+#include <Security/SecItemPriv.h>
+
+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);
+}