2 * Copyright (c) 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 // Test syncing between SecItemDataSource and SOSTestDataSource
27 #include "SOSTestDevice.h"
28 #include "SOSTestDataSource.h"
29 #include <test/testmore.h>
31 #include <SecureObjectSync/SOSEngine.h>
32 #include <SecureObjectSync/SOSPeer.h>
33 #include <Security/SecBase64.h>
34 #include <Security/SecItem.h>
35 #include <Security/SecItemPriv.h>
36 #include <corecrypto/ccsha2.h>
37 #include <securityd/SecItemServer.h>
38 #include <securityd/SecItemDataSource.h>
39 #include <utilities/SecCFWrappers.h>
40 #include <utilities/SecFileLocations.h>
41 #include <utilities/SecIOFormat.h>
44 #include <AssertMacros.h>
46 CFStringRef
SOSMessageCopyDigestHex(SOSMessageRef message
) {
47 uint8_t digest
[CCSHA1_OUTPUT_SIZE
];
48 // TODO: Pass in real sequenceNumber.
49 CFDataRef msgData
= SOSMessageCreateData(message
, 0, NULL
);
50 if (!msgData
) return NULL
;
51 ccdigest(ccsha1_di(), CFDataGetLength(msgData
), CFDataGetBytePtr(msgData
), digest
);
52 CFMutableStringRef hex
= CFStringCreateMutable(0, 2 * sizeof(digest
));
53 for (unsigned int ix
= 0; ix
< sizeof(digest
); ++ix
) {
54 CFStringAppendFormat(hex
, 0, CFSTR("%02X"), digest
[ix
]);
56 CFReleaseSafe(msgData
);
60 static void SOSTestDeviceDestroy(CFTypeRef cf
) {
61 SOSTestDeviceRef td
= (SOSTestDeviceRef
)cf
;
62 CFReleaseSafe(td
->peers
);
64 SOSDataSourceRelease(td
->ds
, NULL
);
66 td
->dsf
->release(td
->dsf
);
67 CFReleaseSafe(td
->db
);
70 CFStringRef
SOSTestDeviceGetID(SOSTestDeviceRef td
) {
71 CFStringRef engineID
= NULL
;
72 SOSEngineRef engine
= SOSDataSourceGetSharedEngine(td
->ds
, NULL
);
74 engineID
= SOSEngineGetMyID(engine
);
78 void SOSTestDeviceForEachPeerID(SOSTestDeviceRef td
, void(^peerBlock
)(CFStringRef peerID
, bool *stop
)) {
81 CFArrayForEachC(td
->peers
, peer
) {
82 peerBlock(SOSPeerGetID(peer
), &stop
);
88 static CFStringRef
SOSTestDeviceCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
89 SOSTestDeviceRef td
= (SOSTestDeviceRef
)cf
;
90 CFMutableStringRef result
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
91 CFStringAppendFormat(result
, NULL
, CFSTR("<SOSTestDevice %@"), td
->ds
->engine
);
92 SOSTestDeviceForEachPeerID(td
, ^(CFStringRef peerID
, bool *stop
) {
93 SOSPeerRef peer
= SOSPeerCreateWithEngine(td
->ds
->engine
, peerID
);
94 CFStringAppendFormat(result
, NULL
, CFSTR("\n%@"), peer
);
97 CFStringAppendFormat(result
, NULL
, CFSTR(">"));
101 CFGiblisFor(SOSTestDevice
)
103 static SOSTestDeviceRef
SOSTestDeviceCreateInternal(CFAllocatorRef allocator
, CFStringRef engineID
) {
104 SOSTestDeviceRef td
= CFTypeAllocate(SOSTestDevice
, struct __OpaqueSOSTestDevice
, allocator
);
105 td
->peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
110 SOSTestDeviceRef
SOSTestDeviceCreateWithDb(CFAllocatorRef allocator
, CFStringRef engineID
, SecDbRef db
) {
111 setup("create device");
112 SOSTestDeviceRef td
= SOSTestDeviceCreateInternal(allocator
, engineID
);
113 CFRetainAssign(td
->db
, db
);
114 td
->dsf
= SecItemDataSourceFactoryGetShared(td
->db
);
115 CFArrayRef ds_names
= td
->dsf
->copy_names(td
->dsf
);
116 CFErrorRef error
= NULL
;
117 if (ds_names
&& CFArrayGetCount(ds_names
) > 0) {
118 CFStringRef sname
= CFArrayGetValueAtIndex(ds_names
, 0);
119 ok (td
->ds
= td
->dsf
->create_datasource(td
->dsf
, sname
, &error
), "%@ create datasource \"%@\" [error: %@]", engineID
, sname
, error
);
120 CFReleaseNull(error
);
122 CFReleaseNull(ds_names
);
123 assert(td
->ds
); // Shut up static analyzer and test generally run in debug mode anyway
125 SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td
->ds
, NULL
), engineID
, NULL
, NULL
);
129 SOSTestDeviceRef
SOSTestDeviceCreateWithDbNamed(CFAllocatorRef allocator
, CFStringRef engineID
, CFStringRef dbName
) {
130 CFURLRef url
= SecCopyURLForFileInKeychainDirectory(dbName
);
131 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
132 SecDbRef db
= SecKeychainDbCreate(path
);
133 SOSTestDeviceRef td
= SOSTestDeviceCreateWithDb(allocator
, engineID
, db
);
140 SOSTestDeviceRef
SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator
, CFStringRef engineID
) {
141 setup("create device");
142 SOSTestDeviceRef td
= SOSTestDeviceCreateInternal(allocator
, engineID
);
144 td
->ds
= SOSTestDataSourceCreate();
145 CFErrorRef error
= NULL
;
146 ok(td
->ds
->engine
= SOSEngineCreate(td
->ds
, &error
), "create engine: %@", error
);
147 SOSEngineCircleChanged(td
->ds
->engine
, engineID
, NULL
, NULL
);
148 CFReleaseNull(error
);
152 SOSTestDeviceRef
SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td
, CFArrayRef peerIDs
, CFIndex version
) {
153 setup("create device");
154 CFStringRef engineID
= SOSTestDeviceGetID(td
);
156 CFMutableArrayRef trustedPeersIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
157 CFErrorRef error
= NULL
;
158 CFArrayForEachC(peerIDs
, peerID
) {
159 if (isString(peerID
) && !CFEqualSafe(peerID
, engineID
)) {
161 ok(peer
= SOSPeerCreateSimple(td
->ds
->engine
, peerID
, version
, &error
), "create peer: %@", error
);
162 CFReleaseNull(error
);
163 CFArrayAppendValue(td
->peers
, peer
);
164 CFArrayAppendValue(trustedPeersIDs
, peerID
);
168 SOSEngineCircleChanged(td
->ds
->engine
, engineID
, trustedPeersIDs
, NULL
);
169 CFArrayForEachC(trustedPeersIDs
, peerID
) {
170 ok(SOSEnginePeerDidConnect(td
->ds
->engine
, peerID
, &error
), "tell %@ %@ connected: %@", engineID
, peerID
, error
);
171 CFReleaseNull(error
);
173 CFReleaseSafe(trustedPeersIDs
);
177 SOSTestDeviceRef
SOSTestDeviceSetMute(SOSTestDeviceRef td
, bool mute
) {
182 bool SOSTestDeviceIsMute(SOSTestDeviceRef td
) {
186 CFDataRef
SOSTestDeviceCreateMessage(SOSTestDeviceRef td
, CFStringRef peerID
) {
187 setup("create message");
188 CFErrorRef error
= NULL
;
189 SOSEnginePeerMessageSentBlock sent
= NULL
;
191 ok(msgData
= SOSEngineCreateMessageToSyncToPeer(td
->ds
->engine
, peerID
, &sent
, &error
),
192 "create message to %@: %@", peerID
, error
);
200 CFDictionaryRef
SOSTestDeviceCreateMessages(SOSTestDeviceRef td
) {
201 CFTypeRef peer
= NULL
;
202 CFMutableDictionaryRef messages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
203 CFArrayForEachC(td
->peers
, peer
) {
204 CFStringRef peerID
= SOSPeerGetID((SOSPeerRef
)peer
);
205 CFDataRef msg
= SOSTestDeviceCreateMessage(td
, peerID
);
207 CFDictionaryAddValue(messages
, peerID
, msg
);
215 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td
, CFStringRef peerID
, CFDataRef msgData
) {
216 setup("handle message");
217 if (!msgData
) return false;
218 CFErrorRef error
= NULL
;
220 SOSMessageRef message
;
222 ok(message
= SOSMessageCreateWithData(kCFAllocatorDefault
, msgData
, &error
), "decode message %@: %@", msgData
, error
);
223 CFReleaseNull(error
);
224 pass("handeling %@->%@ %@", peerID
, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td
->ds
, &error
)), message
);
225 ok(handled
= SOSEngineHandleMessage(SOSDataSourceGetSharedEngine(td
->ds
, &error
), peerID
, msgData
, &error
),
226 "handled from %@ %@: %@", peerID
, message
, error
);
227 CFReleaseNull(error
);
229 CFReleaseNull(message
);
233 void SOSTestDeviceAddGenericItem(SOSTestDeviceRef td
, CFStringRef account
, CFStringRef server
) {
234 __block CFErrorRef error
= NULL
;
235 if (!SOSDataSourceWithAPI(td
->ds
, true, &error
, ^(SOSTransactionRef txn
, bool *commit
) {
236 SOSObjectRef object
= SOSDataSourceCreateGenericItem(td
->ds
, account
, server
);
237 ok(SOSDataSourceMergeObject(td
->ds
, txn
, object
, NULL
, &error
), "%@ added API object %@", SOSTestDeviceGetID(td
), error
? (CFTypeRef
)error
: (CFTypeRef
)CFSTR("ok"));
238 CFReleaseSafe(object
);
239 CFReleaseNull(error
);
241 fail("ds transaction %@", error
);
242 CFReleaseNull(error
);
245 void SOSTestDeviceAddRemoteGenericItem(SOSTestDeviceRef td
, CFStringRef account
, CFStringRef server
) {
246 __block CFErrorRef error
= NULL
;
247 if (!SOSDataSourceWithAPI(td
->ds
, false, &error
, ^(SOSTransactionRef txn
, bool *commit
) {
248 SOSObjectRef object
= SOSDataSourceCreateGenericItem(td
->ds
, account
, server
);
249 ok(SOSDataSourceMergeObject(td
->ds
, txn
, object
, NULL
, &error
), "%@ added remote object %@", SOSTestDeviceGetID(td
), error
? (CFTypeRef
)error
: (CFTypeRef
)CFSTR("ok"));
250 CFReleaseSafe(object
);
251 CFReleaseNull(error
);
253 fail("ds transaction %@", error
);
254 CFReleaseNull(error
);
257 bool SOSTestDeviceAddGenericItems(SOSTestDeviceRef td
, CFIndex count
, CFStringRef account
, CFStringRef server
) {
258 __block
bool didAdd
= false;
259 __block CFErrorRef error
= NULL
;
260 if (!SOSDataSourceWithAPI(td
->ds
, true, &error
, ^(SOSTransactionRef txn
, bool *commit
) {
263 for (; success
&& ix
< count
; ++ix
) {
264 CFStringRef accountStr
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@%" PRIdCFIndex
), account
, ix
);
265 SOSObjectRef object
= SOSDataSourceCreateGenericItem(td
->ds
, accountStr
, server
);
266 success
= SOSDataSourceMergeObject(td
->ds
, txn
, object
, NULL
, &error
);
267 CFReleaseSafe(object
);
269 ok(success
, "%@ added %" PRIdCFIndex
" API objects %@", SOSTestDeviceGetID(td
), ix
, error
? (CFTypeRef
)error
: (CFTypeRef
)CFSTR("ok"));
270 didAdd
= success
&& ix
== count
;
271 CFReleaseNull(error
);
273 fail("ds transaction %@", error
);
274 CFReleaseNull(error
);
278 CFMutableDictionaryRef
SOSTestDeviceListCreate(bool realDb
, CFIndex version
, CFArrayRef deviceIDs
) {
279 CFMutableDictionaryRef testDevices
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
280 CFStringRef deviceID
;
281 CFArrayForEachC(deviceIDs
, deviceID
) {
282 SOSTestDeviceRef device
;
284 device
= SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault
, deviceID
);
286 device
= SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault
, deviceID
, deviceID
);
287 SOSTestDeviceSetPeerIDs(device
, deviceIDs
, version
);
288 CFDictionarySetValue(testDevices
, deviceID
, device
);
289 CFReleaseSafe(device
);
291 CFDictionarySetValue(testDevices
, CFSTR("@devicesIDs"), deviceIDs
);
295 void SOSTestDeviceListSync(const char *name
, const char *test_directive
, const char *test_reason
, CFMutableDictionaryRef testDevices
, bool(^pre
)(SOSTestDeviceRef source
, SOSTestDeviceRef dest
), bool(^post
)(SOSTestDeviceRef source
, SOSTestDeviceRef dest
, SOSMessageRef message
)) {
296 CFArrayRef deviceIDs
= (CFArrayRef
)CFDictionaryGetValue(testDevices
, CFSTR("@devicesIDs"));
297 const CFIndex edgeCount
= CFArrayGetCount(deviceIDs
) * (CFArrayGetCount(deviceIDs
) - 1);
298 CFIndex deviceIX
= 0;
299 __block CFIndex noMsgSentCount
= 0;
300 __block CFIndex msgSentSinceLastChangeCount
= 0;
301 __block
bool done
= false;
303 CFStringRef sourceID
= (CFStringRef
)CFArrayGetValueAtIndex(deviceIDs
, deviceIX
++);
304 if (deviceIX
>= CFArrayGetCount(deviceIDs
))
307 SOSTestDeviceRef source
= (SOSTestDeviceRef
)CFDictionaryGetValue(testDevices
, sourceID
);
308 SOSTestDeviceForEachPeerID(source
, ^(CFStringRef destID
, bool *stop
) {
309 SOSTestDeviceRef dest
= (SOSTestDeviceRef
)CFDictionaryGetValue(testDevices
, destID
);
312 if (pre(source
, dest
))
313 msgSentSinceLastChangeCount
= 0;
315 CFDataRef msg
= SOSTestDeviceCreateMessage(source
, destID
);
316 SOSMessageRef message
= NULL
;
317 bool handled
= false;
318 msgSentSinceLastChangeCount
++;
319 if (msg
&& CFDataGetLength(msg
) > 0) {
321 handled
= SOSTestDeviceHandleMessage(dest
, sourceID
, msg
);
326 CFErrorRef error
= NULL
;
327 message
= SOSMessageCreateWithData(kCFAllocatorDefault
, msg
, &error
);
328 ok(handled
, "%s %@->%@ %@", name
, sourceID
, destID
, message
);
329 CFReleaseNull(error
);
332 SOSManifestRef sourceManifest
= SOSEngineCopyManifest(SOSDataSourceGetSharedEngine(source
->ds
, NULL
), NULL
);
333 pass("%s %@->%@ done L:%@", name
, sourceID
, destID
, sourceManifest
);
334 CFReleaseSafe(sourceManifest
);
336 //msgSentSinceLastChangeCount = 0;
340 if (post(source
, dest
, message
))
341 msgSentSinceLastChangeCount
= 0;
343 CFReleaseNull(message
);
345 if (noMsgSentCount
>= edgeCount
) {
347 } else if (msgSentSinceLastChangeCount
>= 9 * edgeCount
+ 1) {
348 fail("%s %" PRIdCFIndex
" peers never stopped syncing %" PRIdCFIndex
" messages since last change", name
, CFArrayGetCount(deviceIDs
), msgSentSinceLastChangeCount
);
355 bool SOSTestDeviceListInSync(const char *name
, const char *test_directive
, const char *test_reason
, CFMutableDictionaryRef testDevices
) {
357 CFArrayRef deviceIDs
= (CFArrayRef
)CFDictionaryGetValue(testDevices
, CFSTR("@devicesIDs"));
358 CFStringRef sourceID
= NULL
;
359 SOSManifestRef sourceManifest
= NULL
;
360 CFStringRef currentID
;
361 CFArrayForEachC(deviceIDs
, currentID
) {
362 SOSTestDeviceRef source
= (SOSTestDeviceRef
)CFDictionaryGetValue(testDevices
, currentID
);
363 SOSManifestRef manifest
= SOSEngineCopyManifest(SOSDataSourceGetSharedEngine(source
->ds
, NULL
), NULL
);
364 if (!sourceManifest
) {
365 sourceManifest
= CFRetainSafe(manifest
);
366 sourceID
= currentID
;
367 } else if (!CFEqual(manifest
, sourceManifest
)) {
368 fail("%s %@ manifest %@ != %@ manifest %@", name
, currentID
, manifest
, sourceID
, sourceManifest
);
371 CFReleaseSafe(manifest
);
373 CFReleaseSafe(sourceManifest
);
375 pass("%s all peers in sync", name
);
379 void SOSTestDeviceListTestSync(const char *name
, const char *test_directive
, const char *test_reason
, CFIndex version
, bool use_db
,
380 bool(^pre
)(SOSTestDeviceRef source
, SOSTestDeviceRef dest
),
381 bool(^post
)(SOSTestDeviceRef source
, SOSTestDeviceRef dest
, SOSMessageRef message
), ...) {
383 va_start(args
, post
);
384 // Optionally prefix each peer with name to make them more unique.
385 CFArrayRef deviceIDs
= CFArrayCreateForVC(kCFAllocatorDefault
, &kCFTypeArrayCallBacks
, args
);
386 CFMutableDictionaryRef testDevices
= SOSTestDeviceListCreate(use_db
, version
, deviceIDs
);
387 CFReleaseSafe(deviceIDs
);
388 SOSTestDeviceListSync(name
, test_directive
, test_reason
, testDevices
, pre
, post
);
389 SOSTestDeviceListInSync(name
, test_directive
, test_reason
, testDevices
);
390 CFReleaseSafe(testDevices
);