2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "SOSTestDataSource.h"
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>
38 CFStringRef sSOSDataSourceErrorDomain
= CFSTR("com.apple.datasource");
40 typedef struct SOSTestDataSource
*SOSTestDataSourceRef
;
42 struct SOSTestDataSource
{
43 struct SOSDataSource ds
;
47 CFMutableDictionaryRef d2database
;
48 CFMutableDictionaryRef p2database
;
49 CFMutableDictionaryRef statedb
;
50 uint8_t manifest_digest
[SOSDigestSize
];
53 struct SOSDigestVector removals
;
54 struct SOSDigestVector additions
;
55 SOSDataSourceNotifyBlock notifyBlock
;
58 typedef struct SOSTestDataSourceFactory
*SOSTestDataSourceFactoryRef
;
60 struct SOSTestDataSourceFactory
{
61 struct SOSDataSourceFactory dsf
;
62 CFMutableDictionaryRef data_sources
;
66 /* DataSource protocol. */
67 static SOSManifestRef
copy_manifest(SOSDataSourceRef data_source
, CFErrorRef
*error
) {
68 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
70 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
71 CFDictionaryForEach(ds
->d2database
, ^(const void *key
, const void *value
) {
72 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr((CFDataRef
)key
));
74 SOSDigestVectorSort(&dv
);
75 SOSManifestRef manifest
= SOSManifestCreateWithDigestVector(&dv
, error
);
76 SOSDigestVectorFree(&dv
);
77 ccdigest(ccsha1_di(), SOSManifestGetSize(manifest
), SOSManifestGetBytePtr(manifest
), ds
->manifest_digest
);
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
;
86 __block
bool result
= true;
87 SOSManifestForEach(manifest
, ^(CFDataRef key
, bool *stop
) {
88 handle_object(key
, (SOSObjectRef
)CFDictionaryGetValue(ds
->d2database
, key
), stop
);
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
);
102 static SOSObjectRef
createWithPropertyList(CFDictionaryRef plist
, CFErrorRef
*error
) {
103 return (SOSObjectRef
)CFDictionaryCreateCopy(kCFAllocatorDefault
, plist
);
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
);
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
);
117 } else if (error
&& *error
== NULL
) {
118 *error
= CFErrorCreate(0, sSOSDataSourceErrorDomain
, kSOSDataSourceObjectMallocFailed
, NULL
);
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
);
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
);
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
);
140 CFDataRef digest
= NULL
;
142 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
148 static CFDataRef
copyPrimaryKey(SOSObjectRef object
, CFErrorRef
*error
) {
149 CFMutableDictionaryRef ocopy
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
150 CFTypeRef pkNames
[] = {
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
);
180 CFDataRef der
= SOSObjectCopyDER((SOSObjectRef
)ocopy
, error
);
182 CFDataRef digest
= NULL
;
184 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
190 static CFDictionaryRef
copyPropertyList(SOSObjectRef object
, CFErrorRef
*error
) {
191 return (CFDictionaryRef
) CFRetain(object
);
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
;
200 m1
= CFDictionaryGetValue(dict1
, kSecAttrModificationDate
);
201 m2
= CFDictionaryGetValue(dict2
, kSecAttrModificationDate
);
202 switch (CFDateCompare(m1
, m2
, NULL
)) {
203 case kCFCompareGreaterThan
:
204 result
= (SOSObjectRef
)dict1
;
206 case kCFCompareLessThan
:
207 result
= (SOSObjectRef
)dict2
;
209 case kCFCompareEqualTo
:
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
;
219 case kCFCompareLessThan
:
220 result
= (SOSObjectRef
)dict1
;
223 CFReleaseSafe(digest2
);
224 CFReleaseSafe(digest1
);
228 CFRetainSafe(result
);
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
);
237 SOSObjectRef myObject
= (SOSObjectRef
)CFDictionaryGetValue(ds
->p2database
, pk
);
239 SOSObjectRef merged
= copyMergedObject(object
, myObject
, error
);
240 if (CFEqualSafe(merged
, myObject
)) {
241 mr
= kSOSMergeLocalObject
;
242 } else if (CFEqualSafe(merged
, object
)) {
243 mr
= kSOSMergePeersObject
;
246 *createdObject
= merged
;
249 mr
= kSOSMergeCreatedObject
;
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
));
260 CFDictionarySetValue(ds
->p2database
, pk
, merged
);
262 CFReleaseSafe(merged
);
264 SOSTestDataSourceAddObject((SOSDataSourceRef
)ds
, object
, error
);
265 mr
= kSOSMergePeersObject
;
271 static CFStringRef
dsGetName(SOSDataSourceRef ds
) {
272 return CFSTR("The sky is made of butterflies");
275 static void dsSetNotifyPhaseBlock(SOSDataSourceRef ds
, SOSDataSourceNotifyBlock notifyBlock
) {
276 ((SOSTestDataSourceRef
)ds
)->notifyBlock
= Block_copy(notifyBlock
);
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
);
287 static bool dsWith(SOSDataSourceRef ds
, CFErrorRef
*error
, SOSDataSourceTransactionSource source
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
)) {
288 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
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
);
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
);
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
313 static CFDictionaryRef
objectCopyBackup(SOSObjectRef object
, uint64_t handle
, CFErrorRef
*error
) {
314 // OMG We failed without an error.
319 SOSDataSourceRef
SOSTestDataSourceCreate(void) {
320 SOSTestDataSourceRef ds
= calloc(1, sizeof(struct SOSTestDataSource
));
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
;
331 ds
->ds
.dsMergeObject
= mergeObject
;
332 ds
->ds
.dsSetStateWithKey
= dsSetStateWithKey
;
333 ds
->ds
.dsRestoreObject
= dsRestoreObject
;
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
;
341 ds
->d2database
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
342 ds
->p2database
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
343 ds
->statedb
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
346 return (SOSDataSourceRef
)ds
;
349 static CFArrayRef
SOSTestDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory
)
351 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
352 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
354 CFDictionaryForEach(dsf
->data_sources
, ^(const void*key
, const void*value
) { CFArrayAppendValue(result
, key
); });
359 static SOSDataSourceRef
SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory
, CFStringRef dataSourceName
, CFErrorRef
*error
)
361 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
363 return (SOSDataSourceRef
) CFDictionaryGetValue(dsf
->data_sources
, dataSourceName
);
366 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory
)
368 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
370 CFReleaseNull(dsf
->data_sources
);
374 SOSDataSourceFactoryRef
SOSTestDataSourceFactoryCreate() {
375 SOSTestDataSourceFactoryRef dsf
= calloc(1, sizeof(struct SOSTestDataSourceFactory
));
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
);
385 static bool do_nothing(SOSDataSourceRef ds
, CFErrorRef
*error
) {
389 void SOSTestDataSourceFactoryAddDataSource(SOSDataSourceFactoryRef factory
, CFStringRef name
, SOSDataSourceRef ds
)
391 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
393 // TODO This hack sucks. It leaks now.
394 ds
->dsRelease
= do_nothing
;
396 CFDictionarySetValue(dsf
->data_sources
, name
, ds
);
400 SOSMergeResult
SOSTestDataSourceAddObject(SOSDataSourceRef data_source
, SOSObjectRef object
, CFErrorRef
*error
) {
401 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
403 CFDataRef key
= copyDigest(object
, error
);
404 CFDataRef pk
= copyPrimaryKey(object
, error
);
406 SOSObjectRef myObject
= (SOSObjectRef
)CFDictionaryGetValue(ds
->p2database
, pk
);
407 SOSObjectRef merged
= NULL
;
409 merged
= copyMergedObject(object
, myObject
, error
);
416 if (!CFEqualSafe(merged
, myObject
)) {
418 CFDataRef myKey
= copyDigest(myObject
, error
);
419 CFDictionaryRemoveValue(ds
->d2database
, myKey
);
420 SOSDigestVectorAppend(&ds
->removals
, CFDataGetBytePtr(myKey
));
421 CFReleaseSafe(myKey
);
423 CFDictionarySetValue(ds
->d2database
, key
, merged
);
424 CFDictionarySetValue(ds
->p2database
, pk
, merged
);
425 SOSDigestVectorAppend(&ds
->additions
, CFDataGetBytePtr(key
));
436 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source
, CFDataRef key
, CFErrorRef
*error
) {
437 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
441 CFMutableDictionaryRef
SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source
) {
442 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
443 return ds
->d2database
;
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
) {
449 CFNumberRef zero
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &value
);
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
,
469 CFErrorRef localError
= NULL
;
470 SOSObjectRef object
= ds
->objectCreateWithPropertyList(dict
, &localError
);
472 secerror("createWithPropertyList: %@ failed: %@", dict
, localError
);
473 CFRelease(localError
);
479 SOSObjectRef
SOSDataSourceCreateGenericItem(SOSDataSourceRef ds
, CFStringRef account
, CFStringRef service
) {
480 return SOSDataSourceCreateGenericItemWithData(ds
, account
, service
, false, NULL
);
483 SOSObjectRef
SOSDataSourceCopyObject(SOSDataSourceRef ds
, SOSObjectRef match
, CFErrorRef
*error
)
485 __block SOSObjectRef result
= NULL
;
487 CFDataRef digest
= SOSObjectCopyDigest(ds
, match
, error
);
488 SOSManifestRef manifest
= NULL
;
490 require(digest
, exit
);
491 manifest
= SOSManifestCreateWithData(digest
, error
);
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
);
498 } else if (result
== NULL
) {
499 result
= CFRetainSafe(object
);
504 CFReleaseNull(manifest
);
505 CFReleaseNull(digest
);