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 <Security/SecureObjectSync/SOSDataSource.h>
29 #include <Security/SecureObjectSync/SOSDigestVector.h>
30 #include <Security/SecureObjectSync/SOSViews.h>
32 #include <utilities/array_size.h>
33 #include <utilities/der_plist.h>
34 #include <utilities/SecCFError.h>
35 #include <utilities/SecCFWrappers.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecItemPriv.h>
38 #include <AssertMacros.h>
40 CFStringRef sSOSDataSourceErrorDomain
= CFSTR("com.apple.datasource");
42 typedef struct SOSTestDataSource
*SOSTestDataSourceRef
;
44 struct SOSTestDataSource
{
45 struct SOSDataSource ds
;
49 CFMutableDictionaryRef d2database
;
50 CFMutableDictionaryRef p2database
;
51 CFMutableDictionaryRef statedb
;
52 uint8_t manifest_digest
[SOSDigestSize
];
55 CFMutableArrayRef changes
;
56 SOSDataSourceNotifyBlock notifyBlock
;
59 typedef struct SOSTestDataSourceFactory
*SOSTestDataSourceFactoryRef
;
61 struct SOSTestDataSourceFactory
{
62 struct SOSDataSourceFactory dsf
;
63 CFMutableDictionaryRef data_sources
;
67 /* DataSource protocol. */
68 static SOSManifestRef
dsCopyManifestWithViewNameSet(SOSDataSourceRef data_source
, CFSetRef viewNameSet
, CFErrorRef
*error
) {
69 if (!CFSetContainsValue(viewNameSet
, kSOSViewKeychainV0
))
70 return SOSManifestCreateWithData(NULL
, error
);
72 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
74 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
75 CFDictionaryForEach(ds
->d2database
, ^(const void *key
, const void *value
) {
76 SOSDigestVectorAppend(&dv
, CFDataGetBytePtr((CFDataRef
)key
));
78 SOSDigestVectorSort(&dv
);
79 SOSManifestRef manifest
= SOSManifestCreateWithDigestVector(&dv
, error
);
80 SOSDigestVectorFree(&dv
);
81 ccdigest(ccsha1_di(), SOSManifestGetSize(manifest
), SOSManifestGetBytePtr(manifest
), ds
->manifest_digest
);
87 static bool foreach_object(SOSDataSourceRef data_source
, SOSTransactionRef txn
, SOSManifestRef manifest
, CFErrorRef
*error
, void (^handle_object
)(CFDataRef key
, SOSObjectRef object
, bool *stop
)) {
88 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
90 __block
bool result
= true;
91 SOSManifestForEach(manifest
, ^(CFDataRef key
, bool *stop
) {
92 handle_object(key
, (SOSObjectRef
)CFDictionaryGetValue(ds
->d2database
, key
), stop
);
97 static bool dispose(SOSDataSourceRef data_source
, CFErrorRef
*error
) {
98 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
99 CFReleaseSafe(ds
->d2database
);
100 CFReleaseSafe(ds
->p2database
);
101 CFReleaseSafe(ds
->statedb
);
102 CFReleaseSafe(ds
->changes
);
107 static SOSObjectRef
createWithPropertyList(CFDictionaryRef plist
, CFErrorRef
*error
) {
108 return (SOSObjectRef
)CFDictionaryCreateCopy(kCFAllocatorDefault
, plist
);
111 static CFDataRef
SOSObjectCopyDER(SOSObjectRef object
, CFErrorRef
*error
) {
112 CFDictionaryRef dict
= (CFDictionaryRef
)object
;
113 size_t size
= der_sizeof_plist(dict
, error
);
114 CFMutableDataRef data
= CFDataCreateMutable(0, size
);
116 CFDataSetLength(data
, size
);
117 uint8_t *der
= (uint8_t *)CFDataGetMutableBytePtr(data
);
118 uint8_t *der_end
= der
+ size
;
119 der_end
= der_encode_plist(dict
, error
, der
, der_end
);
120 assert(der_end
== der
);
122 } else if (error
&& *error
== NULL
) {
123 *error
= CFErrorCreate(0, sSOSDataSourceErrorDomain
, kSOSDataSourceObjectMallocFailed
, NULL
);
128 static CFDataRef
ccdigest_copy_data(const struct ccdigest_info
*di
, size_t len
,
129 const void *data
, CFErrorRef
*error
) {
130 CFMutableDataRef digest
= CFDataCreateMutable(0, di
->output_size
);
132 CFDataSetLength(digest
, di
->output_size
);
133 ccdigest(di
, len
, data
, CFDataGetMutableBytePtr(digest
));
134 } else if (error
&& *error
== NULL
) {
135 *error
= CFErrorCreate(0, sSOSDataSourceErrorDomain
, kSOSDataSourceObjectMallocFailed
, NULL
);
140 static CFDataRef
copyDigest(SOSObjectRef object
, CFErrorRef
*error
) {
141 CFMutableDictionaryRef ocopy
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, (CFDictionaryRef
)object
);
142 CFDictionaryRemoveValue(ocopy
, kSecClass
);
143 CFDataRef der
= SOSObjectCopyDER((SOSObjectRef
)ocopy
, error
);
145 CFDataRef digest
= NULL
;
147 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
153 static CFDataRef
copyPrimaryKey(SOSObjectRef object
, CFErrorRef
*error
) {
154 CFMutableDictionaryRef ocopy
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
155 CFTypeRef pkNames
[] = {
179 CFSetRef pkAttrs
= CFSetCreate(kCFAllocatorDefault
, pkNames
, array_size(pkNames
), &kCFTypeSetCallBacks
);
180 CFDictionaryForEach((CFDictionaryRef
)object
, ^(const void *key
, const void *value
) {
181 if (CFSetContainsValue(pkAttrs
, key
))
182 CFDictionaryAddValue(ocopy
, key
, value
);
185 CFDataRef der
= SOSObjectCopyDER((SOSObjectRef
)ocopy
, error
);
187 CFDataRef digest
= NULL
;
189 digest
= ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
195 static CFDictionaryRef
copyPropertyList(SOSObjectRef object
, CFErrorRef
*error
) {
196 return (CFDictionaryRef
) CFRetain(object
);
199 // Return the newest object
200 static SOSObjectRef
copyMergedObject(SOSObjectRef object1
, SOSObjectRef object2
, CFErrorRef
*error
) {
201 CFDictionaryRef dict1
= (CFDictionaryRef
)object1
;
202 CFDictionaryRef dict2
= (CFDictionaryRef
)object2
;
203 SOSObjectRef result
= NULL
;
205 m1
= CFDictionaryGetValue(dict1
, kSecAttrModificationDate
);
206 m2
= CFDictionaryGetValue(dict2
, kSecAttrModificationDate
);
207 switch (CFDateCompare(m1
, m2
, NULL
)) {
208 case kCFCompareGreaterThan
:
209 result
= (SOSObjectRef
)dict1
;
211 case kCFCompareLessThan
:
212 result
= (SOSObjectRef
)dict2
;
214 case kCFCompareEqualTo
:
216 // Return the item with the smallest digest.
217 CFDataRef digest1
= copyDigest(object1
, error
);
218 CFDataRef digest2
= copyDigest(object2
, error
);
219 if (digest1
&& digest2
) switch (CFDataCompare(digest1
, digest2
)) {
220 case kCFCompareGreaterThan
:
221 case kCFCompareEqualTo
:
222 result
= (SOSObjectRef
)dict2
;
224 case kCFCompareLessThan
:
225 result
= (SOSObjectRef
)dict1
;
228 CFReleaseSafe(digest2
);
229 CFReleaseSafe(digest1
);
233 CFRetainSafe(result
);
237 static SOSMergeResult
mergeObject(SOSTransactionRef txn
, SOSObjectRef object
, SOSObjectRef
*mergedObject
, CFErrorRef
*error
) {
238 SOSTestDataSourceRef ds
= (SOSTestDataSourceRef
)txn
;
239 SOSMergeResult mr
= kSOSMergeFailure
;
240 CFDataRef pk
= copyPrimaryKey(object
, error
);
242 SOSObjectRef myObject
= (SOSObjectRef
)CFDictionaryGetValue(ds
->p2database
, pk
);
244 SOSObjectRef merged
= copyMergedObject(object
, myObject
, error
);
245 if (mergedObject
) *mergedObject
= CFRetainSafe(merged
);
246 if (CFEqualSafe(merged
, myObject
)) {
247 mr
= kSOSMergeLocalObject
;
248 } else if (CFEqualSafe(merged
, object
)) {
249 mr
= kSOSMergePeersObject
;
251 mr
= kSOSMergeCreatedObject
;
253 if (mr
!= kSOSMergeLocalObject
) {
254 CFDataRef myKey
= copyDigest(myObject
, error
);
255 CFDictionaryRemoveValue(ds
->d2database
, myKey
);
256 CFReleaseSafe(myKey
);
257 CFDataRef key
= copyDigest(merged
, error
);
258 CFDictionarySetValue(ds
->d2database
, key
, merged
);
259 const void *values
[2] = { myObject
, merged
};
260 CFTypeRef entry
= CFArrayCreate(kCFAllocatorDefault
, values
, 2, &kCFTypeArrayCallBacks
);
262 CFArrayAppendValue(ds
->changes
, entry
);
266 CFDictionarySetValue(ds
->p2database
, pk
, merged
);
268 CFReleaseSafe(merged
);
270 SOSTestDataSourceAddObject((SOSDataSourceRef
)ds
, object
, error
);
271 mr
= kSOSMergePeersObject
;
277 static CFStringRef
dsGetName(SOSDataSourceRef ds
) {
278 return CFSTR("The sky is made of butterflies");
281 static void dsAddNotifyPhaseBlock(SOSDataSourceRef ds
, SOSDataSourceNotifyBlock notifyBlock
) {
282 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
283 assert(tds
->notifyBlock
== NULL
);
284 tds
->notifyBlock
= Block_copy(notifyBlock
);
287 static CFDataRef
dsCopyStateWithKey(SOSDataSourceRef ds
, CFStringRef key
, CFStringRef pdmn
, SOSTransactionRef txn
, CFErrorRef
*error
) {
288 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
289 CFStringRef dbkey
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-%@"), pdmn
, key
);
290 CFDataRef state
= CFDictionaryGetValue(tds
->statedb
, dbkey
);
291 CFReleaseSafe(dbkey
);
292 return CFRetainSafe(state
);
295 static CFDataRef
dsCopyItemDataWithKeys(SOSDataSourceRef data_source
, CFDictionaryRef keys
, CFErrorRef
*error
) {
296 SecError(errSecUnimplemented
, error
, CFSTR("dsCopyItemDataWithKeys on test data source not implemented"));
300 static bool dsWith(SOSDataSourceRef ds
, CFErrorRef
*error
, SOSDataSourceTransactionSource source
, bool onCommitQueue
, void(^transaction
)(SOSTransactionRef txn
, bool *commit
)) {
301 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
303 transaction((SOSTransactionRef
)ds
, &commit
);
304 if (commit
&& ((SOSTestDataSourceRef
)ds
)->notifyBlock
&& (CFArrayGetCount(tds
->changes
))) {
305 ((SOSTestDataSourceRef
)ds
)->notifyBlock(ds
, (SOSTransactionRef
)ds
, kSOSDataSourceTransactionWillCommit
, source
, tds
->changes
);
306 CFArrayRemoveAllValues(tds
->changes
);
311 static bool dsReadWith(SOSDataSourceRef ds
, CFErrorRef
*error
, SOSDataSourceTransactionSource source
, void(^perform
)(SOSTransactionRef txn
)) {
312 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
313 perform((SOSTransactionRef
)tds
);
317 static bool dsSetStateWithKey(SOSDataSourceRef ds
, SOSTransactionRef txn
, CFStringRef pdmn
, CFStringRef key
, CFDataRef state
, CFErrorRef
*error
) {
318 SOSTestDataSourceRef tds
= (SOSTestDataSourceRef
)ds
;
319 CFStringRef dbkey
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-%@"), pdmn
, key
);
320 CFDictionarySetValue(tds
->statedb
, dbkey
, state
);
321 CFReleaseSafe(dbkey
);
325 static bool dsRestoreObject(SOSTransactionRef txn
, uint64_t handle
, CFDictionaryRef item
, CFErrorRef
*error
) {
326 // TODO: Just call merge, probably doesn't belong in protocol at all
331 static CFDictionaryRef
objectCopyBackup(SOSObjectRef object
, uint64_t handle
, CFErrorRef
*error
) {
332 // OMG We failed without an error.
337 SOSDataSourceRef
SOSTestDataSourceCreate(void) {
338 SOSTestDataSourceRef ds
= calloc(1, sizeof(struct SOSTestDataSource
));
340 ds
->ds
.engine
= NULL
;
341 ds
->ds
.dsGetName
= dsGetName
;
342 ds
->ds
.dsAddNotifyPhaseBlock
= dsAddNotifyPhaseBlock
;
343 ds
->ds
.dsCopyManifestWithViewNameSet
= dsCopyManifestWithViewNameSet
;
344 ds
->ds
.dsForEachObject
= foreach_object
;
345 ds
->ds
.dsCopyStateWithKey
= dsCopyStateWithKey
;
346 ds
->ds
.dsCopyItemDataWithKeys
= dsCopyItemDataWithKeys
;
348 ds
->ds
.dsWith
= dsWith
;
349 ds
->ds
.dsRelease
= dispose
;
350 ds
->ds
.dsReadWith
= dsReadWith
;
352 ds
->ds
.dsMergeObject
= mergeObject
;
353 ds
->ds
.dsSetStateWithKey
= dsSetStateWithKey
;
354 ds
->ds
.dsRestoreObject
= dsRestoreObject
;
356 ds
->ds
.objectCopyDigest
= copyDigest
;
357 ds
->ds
.objectCreateWithPropertyList
= createWithPropertyList
;
358 ds
->ds
.objectCopyPropertyList
= copyPropertyList
;
359 ds
->ds
.objectCopyBackup
= objectCopyBackup
;
361 ds
->d2database
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
362 ds
->p2database
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
363 ds
->statedb
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
365 ds
->changes
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
367 return (SOSDataSourceRef
)ds
;
370 static CFStringRef
SOSTestDataSourceFactoryCopyName(SOSDataSourceFactoryRef factory
)
372 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
374 __block CFStringRef result
= NULL
;
375 CFDictionaryForEach(dsf
->data_sources
, ^(const void*key
, const void*value
) { if (isString(key
)) result
= key
; });
377 return CFRetainSafe(result
);
380 static SOSDataSourceRef
SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory
, CFStringRef dataSourceName
, CFErrorRef
*error
)
382 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
384 return (SOSDataSourceRef
) CFDictionaryGetValue(dsf
->data_sources
, dataSourceName
);
387 static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory
)
389 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
391 CFReleaseNull(dsf
->data_sources
);
395 SOSDataSourceFactoryRef
SOSTestDataSourceFactoryCreate() {
396 SOSTestDataSourceFactoryRef dsf
= calloc(1, sizeof(struct SOSTestDataSourceFactory
));
398 dsf
->dsf
.copy_name
= SOSTestDataSourceFactoryCopyName
;
399 dsf
->dsf
.create_datasource
= SOSTestDataSourceFactoryCreateDataSource
;
400 dsf
->dsf
.release
= SOSTestDataSourceFactoryDispose
;
401 dsf
->data_sources
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
406 static bool do_nothing(SOSDataSourceRef ds
, CFErrorRef
*error
) {
410 void SOSTestDataSourceFactorySetDataSource(SOSDataSourceFactoryRef factory
, CFStringRef name
, SOSDataSourceRef ds
)
412 SOSTestDataSourceFactoryRef dsf
= (SOSTestDataSourceFactoryRef
) factory
;
414 // TODO This hack sucks. It leaks now.
415 ds
->dsRelease
= do_nothing
;
417 CFDictionaryRemoveAllValues(dsf
->data_sources
);
418 CFDictionarySetValue(dsf
->data_sources
, name
, ds
);
422 SOSMergeResult
SOSTestDataSourceAddObject(SOSDataSourceRef data_source
, SOSObjectRef object
, CFErrorRef
*error
) {
423 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
425 CFDataRef key
= copyDigest(object
, error
);
426 CFDataRef pk
= copyPrimaryKey(object
, error
);
428 SOSObjectRef myObject
= (SOSObjectRef
)CFDictionaryGetValue(ds
->p2database
, pk
);
429 SOSObjectRef merged
= NULL
;
431 merged
= copyMergedObject(object
, myObject
, error
);
438 if (!CFEqualSafe(merged
, myObject
)) {
440 CFDataRef myKey
= copyDigest(myObject
, error
);
441 CFDictionaryRemoveValue(ds
->d2database
, myKey
);
442 CFReleaseSafe(myKey
);
443 const void *values
[2] = { myObject
, merged
};
444 CFTypeRef entry
= CFArrayCreate(kCFAllocatorDefault
, values
, 2, &kCFTypeArrayCallBacks
);
446 CFArrayAppendValue(ds
->changes
, entry
);
450 CFArrayAppendValue(ds
->changes
, merged
);
452 CFDictionarySetValue(ds
->d2database
, key
, merged
);
453 CFDictionarySetValue(ds
->p2database
, pk
, merged
);
464 bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source
, CFDataRef key
, CFErrorRef
*error
) {
465 //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source;
469 CFMutableDictionaryRef
SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source
) {
470 struct SOSTestDataSource
*ds
= (struct SOSTestDataSource
*)data_source
;
471 return ds
->d2database
;
474 // This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now.
475 SOSObjectRef
SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds
, CFStringRef account
, CFStringRef service
, bool is_tomb
, CFDataRef data
) {
477 CFNumberRef zero
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &value
);
479 CFNumberRef one
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &value
);
480 CFAbsoluteTime timestamp
= 3700000 + (is_tomb
? 1 : 0);
481 CFDateRef now
= CFDateCreate(kCFAllocatorDefault
, timestamp
);
482 CFDictionaryRef dict
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
483 kSecClass
, kSecClassGenericPassword
,
484 kSecAttrSynchronizable
, one
,
485 kSecAttrTombstone
, is_tomb
? one
: zero
,
486 kSecAttrAccount
, account
,
487 kSecAttrService
, service
,
488 kSecAttrCreationDate
, now
,
489 kSecAttrModificationDate
, now
,
490 kSecAttrAccessGroup
, CFSTR("test"),
491 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
492 !is_tomb
&& data
? kSecValueData
: NULL
,data
,
497 CFErrorRef localError
= NULL
;
498 SOSObjectRef object
= ds
->objectCreateWithPropertyList(dict
, &localError
);
500 secerror("createWithPropertyList: %@ failed: %@", dict
, localError
);
501 CFRelease(localError
);
507 SOSObjectRef
SOSDataSourceCreateGenericItem(SOSDataSourceRef ds
, CFStringRef account
, CFStringRef service
) {
508 return SOSDataSourceCreateGenericItemWithData(ds
, account
, service
, false, NULL
);
511 SOSObjectRef
SOSDataSourceCopyObject(SOSDataSourceRef ds
, SOSObjectRef match
, CFErrorRef
*error
)
513 __block SOSObjectRef result
= NULL
;
515 CFDataRef digest
= SOSObjectCopyDigest(ds
, match
, error
);
516 SOSManifestRef manifest
= NULL
;
518 require(digest
, exit
);
519 manifest
= SOSManifestCreateWithData(digest
, error
);
521 SOSDataSourceForEachObject(ds
, NULL
, manifest
, error
, ^void (CFDataRef key
, SOSObjectRef object
, bool *stop
) {
522 if (object
== NULL
) {
523 if (error
&& !*error
) {
524 SecCFCreateErrorWithFormat(kSOSDataSourceObjectNotFoundError
, sSOSDataSourceErrorDomain
, NULL
, error
, 0, CFSTR("key %@ not in database"), key
);
526 } else if (result
== NULL
) {
527 result
= CFRetainSafe(object
);
532 CFReleaseNull(manifest
);
533 CFReleaseNull(digest
);