5 // Created by Michael Brouwer on 9/28/12.
9 #include "SOSTestDataSource.h"
11 #include <corecrypto/ccder.h>
12 #include <SecureObjectSync/SOSEngine.h>
13 #include <utilities/array_size.h>
14 #include <utilities/der_plist.h>
15 #include <utilities/SecCFError.h>
16 #include <utilities/SecCFWrappers.h>
17 #include <Security/SecItemPriv.h>
19 static CFStringRef sErrorDomain
= CFSTR("com.apple.testdatasource");
22 kSOSObjectMallocFailed
= 1,
24 kSOSObjectNotFouncError
= 1,
27 typedef struct SOSTestDataSource
*SOSTestDataSourceRef
;
29 struct SOSTestDataSource
{
30 struct SOSDataSource ds
;
34 CFMutableDictionaryRef database
;
35 uint8_t manifest_digest
[SOSDigestSize
];
39 typedef struct SOSTestDataSourceFactory
*SOSTestDataSourceFactoryRef
;
41 struct SOSTestDataSourceFactory
{
42 struct SOSDataSourceFactory dsf
;
43 CFMutableDictionaryRef data_sources
;
47 /* DataSource protocol. */
48 static bool get_manifest_digest(SOSDataSourceRef data_source
, uint8_t *out_digest
, CFErrorRef
*error
) {
49 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
51 SOSManifestRef mf
= data_source
->copy_manifest(data_source
, error
);
58 memcpy(out_digest
, ds
->manifest_digest
, SOSDigestSize
);
63 static SOSManifestRef
copy_manifest(SOSDataSourceRef data_source
, CFErrorRef
*error
) {
64 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
66 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
67 CFDictionaryForEach(ds
->database
, ^(const void *key
, const void *value
) {
68 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr((CFDataRef
)key
));
70 SOSDigestVectorSort(&dv
);
71 SOSManifestRef manifest
= SOSManifestCreateWithBytes((const uint8_t *)dv
.digest
, dv
.count
* SOSDigestSize
, error
);
72 SOSDigestVectorFree(&dv
);
73 ccdigest(ccsha1_di(), SOSManifestGetSize(manifest
), SOSManifestGetBytePtr(manifest
), ds
->manifest_digest
);
79 static bool foreach_object(SOSDataSourceRef data_source
, SOSManifestRef manifest
, CFErrorRef
*error
, bool (^handle_object
)(SOSObjectRef object
, CFErrorRef
*error
)) {
80 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
82 __block
bool result
= true;
83 SOSManifestForEach(manifest
, ^(CFDataRef key
) {
84 CFDictionaryRef dict
= (CFDictionaryRef
)CFDictionaryGetValue(ds
->database
, key
);
86 result
= result
&& handle_object((SOSObjectRef
)dict
, error
);
90 // TODO: Collect all missing keys in an array and return an single error at the end with all collected keys
91 // Collect all errors as chained errors.
92 CFErrorRef old_error
= *error
;
94 SecCFCreateErrorWithFormat(kSOSObjectNotFouncError
, sErrorDomain
, old_error
, error
, 0, CFSTR("key %@ not in database"), key
);
101 static void dispose(SOSDataSourceRef data_source
) {
102 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
106 static SOSObjectRef
createWithPropertyList(SOSDataSourceRef ds
, CFDictionaryRef plist
, CFErrorRef
*error
) {
107 return (SOSObjectRef
)CFDictionaryCreateCopy(kCFAllocatorDefault
, plist
);
110 static CFDataRef
SOSObjectCopyDER(SOSObjectRef object
, CFErrorRef
*error
) {
111 CFDictionaryRef dict
= (CFDictionaryRef
)object
;
112 size_t size
= der_sizeof_plist(dict
, error
);
113 CFMutableDataRef data
= CFDataCreateMutable(0, size
);
115 CFDataSetLength(data
, size
);
116 uint8_t *der
= (uint8_t *)CFDataGetMutableBytePtr(data
);
117 uint8_t *der_end
= der
+ size
;
118 der_end
= der_encode_plist(dict
, error
, der
, der_end
);
119 assert(der_end
== der
);
120 } else if (error
&& *error
== NULL
) {
121 *error
= CFErrorCreate(0, sErrorDomain
, kSOSObjectMallocFailed
, NULL
);
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
);
130 CFDataSetLength(digest
, di
->output_size
);
131 ccdigest(di
, len
, data
, CFDataGetMutableBytePtr(digest
));
132 } else if (error
&& *error
== NULL
) {
133 *error
= CFErrorCreate(0, sErrorDomain
, kSOSObjectMallocFailed
, NULL
);
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
);
143 CFDataRef digest
= NULL
;
145 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
151 static CFDataRef
copyPrimaryKey(SOSObjectRef object
, CFErrorRef
*error
) {
152 CFMutableDictionaryRef ocopy
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
153 CFTypeRef pkNames
[] = {
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
);
183 CFDataRef der
= SOSObjectCopyDER((SOSObjectRef
)ocopy
, error
);
185 CFDataRef digest
= NULL
;
187 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
193 static CFDictionaryRef
copyPropertyList(SOSObjectRef object
, CFErrorRef
*error
) {
194 return (CFDictionaryRef
) CFRetain(object
);
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
;
203 m1
= CFDictionaryGetValue(dict1
, kSecAttrModificationDate
);
204 m2
= CFDictionaryGetValue(dict2
, kSecAttrModificationDate
);
205 switch (CFDateCompare(m1
, m2
, NULL
)) {
206 case kCFCompareGreaterThan
:
207 result
= (SOSObjectRef
)dict1
;
209 case kCFCompareLessThan
:
210 result
= (SOSObjectRef
)dict2
;
212 case kCFCompareEqualTo
:
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
;
222 case kCFCompareLessThan
:
223 result
= (SOSObjectRef
)dict1
;
226 CFReleaseSafe(digest2
);
227 CFReleaseSafe(digest1
);
231 CFRetainSafe(result
);
235 SOSDataSourceRef
SOSTestDataSourceCreate(void) {
236 SOSTestDataSourceRef ds
= calloc(1, sizeof(struct SOSTestDataSource
));
238 ds
->ds
.get_manifest_digest
= get_manifest_digest
;
239 ds
->ds
.copy_manifest
= copy_manifest
;
240 ds
->ds
.foreach_object
= foreach_object
;
241 ds
->ds
.release
= dispose
;
242 ds
->ds
.add
= SOSTestDataSourceAddObject
;
244 ds
->ds
.createWithPropertyList
= createWithPropertyList
;
245 ds
->ds
.copyDigest
= copyDigest
;
246 ds
->ds
.copyPrimaryKey
= copyPrimaryKey
;
247 ds
->ds
.copyPropertyList
= copyPropertyList
;
248 ds
->ds
.copyMergedObject
= copyMergedObject
;
250 ds
->database
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
253 return (SOSDataSourceRef
)ds
;
256 static CFArrayRef
SOSTestDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory
)
258 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
259 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
261 CFDictionaryForEach(dsf
->data_sources
, ^(const void*key
, const void*value
) { CFArrayAppendValue(result
, key
); });
266 static SOSDataSourceRef
SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory
, CFStringRef dataSourceName
, bool readOnly __unused
, CFErrorRef
*error
)
268 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
270 return (SOSDataSourceRef
) CFDictionaryGetValue(dsf
->data_sources
, dataSourceName
);
273 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory
)
275 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
277 CFReleaseNull(dsf
->data_sources
);
281 SOSDataSourceFactoryRef
SOSTestDataSourceFactoryCreate() {
282 SOSTestDataSourceFactoryRef dsf
= calloc(1, sizeof(struct SOSTestDataSourceFactory
));
284 dsf
->dsf
.copy_names
= SOSTestDataSourceFactoryCopyNames
;
285 dsf
->dsf
.create_datasource
= SOSTestDataSourceFactoryCreateDataSource
;
286 dsf
->dsf
.release
= SOSTestDataSourceFactoryDispose
;
287 dsf
->data_sources
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
292 static void do_nothing(SOSDataSourceRef ds
)
296 void SOSTestDataSourceFactoryAddDataSource(SOSDataSourceFactoryRef factory
, CFStringRef name
, SOSDataSourceRef ds
)
298 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
300 // TODO This hack sucks. It leaks now.
301 ds
->release
= do_nothing
;
303 CFDictionarySetValue(dsf
->data_sources
, name
, ds
);
307 SOSMergeResult
SOSTestDataSourceAddObject(SOSDataSourceRef data_source
, SOSObjectRef object
, CFErrorRef
*error
) {
308 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
310 CFDataRef key
= copyDigest(object
, error
);
312 SOSObjectRef myObject
= (SOSObjectRef
)CFDictionaryGetValue(ds
->database
, key
);
313 SOSObjectRef merged
= NULL
;
315 merged
= copyMergedObject(object
, myObject
, error
);
322 if (!CFEqualSafe(merged
, myObject
)) {
323 CFDictionarySetValue(ds
->database
, key
, merged
);
333 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source
, CFDataRef key
, CFErrorRef
*error
) {
334 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
338 CFMutableDictionaryRef
SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source
) {
339 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
343 // This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now.
344 SOSObjectRef
SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds
, CFStringRef account
, CFStringRef service
, bool is_tomb
, CFDataRef data
) {
345 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
349 CFNumberRef zero
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &value
);
351 CFNumberRef one
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &value
);
352 CFAbsoluteTime timestamp
= 3700000;
353 CFDateRef now
= CFDateCreate(kCFAllocatorDefault
, timestamp
);
354 CFDictionaryRef dict
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
355 kSecClass
, kSecClassGenericPassword
,
356 kSecAttrSynchronizable
, one
,
357 kSecAttrTombstone
, is_tomb
? one
: zero
,
358 kSecAttrAccount
, account
,
359 kSecAttrService
, service
,
360 kSecAttrCreationDate
, now
,
361 kSecAttrModificationDate
, now
,
362 kSecAttrAccessGroup
, CFSTR("test"),
363 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
364 !is_tomb
&& data
? kSecValueData
: NULL
,data
,
369 CFErrorRef localError
= NULL
;
370 SOSObjectRef object
= ds
->createWithPropertyList(ds
, dict
, &localError
);
372 secerror("createWithPropertyList: %@ failed: %@", dict
, localError
);
373 CFRelease(localError
);
380 SOSObjectRef
SOSDataSourceCreateGenericItem(SOSDataSourceRef ds
, CFStringRef account
, CFStringRef service
) {
381 return SOSDataSourceCreateGenericItemWithData(ds
, account
, service
, false, NULL
);