5 // Created by Michael Brouwer on 11/9/12.
6 // Copyright 2012 Apple Inc. All rights reserved.
10 // Test syncing between SecItemDataSource and SOSTestDataSource
12 #include <SecureObjectSync/SOSEngine.h>
13 #include <SecureObjectSync/SOSPeer.h>
15 #include "securityd_regressions.h"
17 #include <corecrypto/ccsha2.h>
18 #include <Security/SecBase64.h>
19 #include <utilities/SecCFWrappers.h>
20 #include <Security/SecItem.h>
21 #include <Security/SecItemPriv.h>
22 #include <securityd/SecItemServer.h>
24 #include <utilities/SecFileLocations.h>
27 #include "SOSTestDataSource.h"
28 #include "SOSTestTransport.h"
30 #include <AssertMacros.h>
32 static int kTestTestCount
= 74;
34 // TODO: Make this shared.
35 static CFStringRef
SOSMessageCopyDigestHex(CFDataRef message
) {
36 uint8_t digest
[CCSHA1_OUTPUT_SIZE
];
37 ccdigest(ccsha1_di(), CFDataGetLength(message
), CFDataGetBytePtr(message
), digest
);
38 CFMutableStringRef hex
= CFStringCreateMutable(0, 2 * sizeof(digest
));
39 for (unsigned int ix
= 0; ix
< sizeof(digest
); ++ix
) {
40 CFStringAppendFormat(hex
, 0, CFSTR("%02X"), digest
[ix
]);
45 static void testsync(const char *name
, const char *test_directive
, const char *test_reason
, void (^aliceInit
)(SOSDataSourceRef ds
), void (^bobInit
)(SOSDataSourceRef ds
), CFStringRef msg
, ...) {
46 CFErrorRef error
= NULL
;
48 /* Setup Alice and Bob's dataSources. */
49 SOSDataSourceFactoryRef aliceDataSourceFactory
= SecItemDataSourceFactoryCreateDefault();
50 SOSDataSourceRef aliceDataSource
= NULL
;
51 CFArrayRef ds_names
= aliceDataSourceFactory
->copy_names(aliceDataSourceFactory
);
52 if (ds_names
&& CFArrayGetCount(ds_names
) > 0) {
53 CFStringRef name
= CFArrayGetValueAtIndex(ds_names
, 0);
54 ok (aliceDataSource
= aliceDataSourceFactory
->create_datasource(aliceDataSourceFactory
, name
, false, &error
), "create datasource \"%@\" [error: %@]", name
, error
);
57 CFReleaseNull(ds_names
);
59 SOSDataSourceRef bobDataSource
= SOSTestDataSourceCreate();
61 /* Setup Alice engine and peer for Alice to talk to Bob */
62 SOSEngineRef aliceEngine
;
63 ok(aliceEngine
= SOSEngineCreate(aliceDataSource
, &error
), "create alice engine: %@", error
);
65 CFStringRef bobID
= CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("Bob-%s"), name
);
67 __block CFDataRef queued_message
= NULL
;
69 SOSPeerSendBlock enqueueMessage
= ^bool (CFDataRef message
, CFErrorRef
*error
) {
71 fail("We already had an unproccessed message");
73 queued_message
= (CFDataRef
) CFRetain(message
);
77 CFDataRef (^dequeueMessage
)() = ^CFDataRef () {
78 CFDataRef result
= queued_message
;
79 queued_message
= NULL
;
85 ok(bobPeer
= SOSPeerCreateSimple(bobID
, kSOSPeerVersion
, &error
, enqueueMessage
),
86 "create peer: %@", error
);
88 /* Setup Bob engine and peer for Bob to talk to Alice */
89 SOSEngineRef bobEngine
;
90 ok(bobEngine
= SOSEngineCreate(bobDataSource
, &error
), "create bob engine: %@", error
);
92 CFStringRef aliceID
= CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("Alice-%s"), name
);
95 ok(alicePeer
= SOSPeerCreateSimple(aliceID
, kSOSPeerVersion
, &error
, enqueueMessage
),
96 "create peer: %@", error
);
99 /* Now call provided setup blocks to populate the dataSources with
100 interesting stuff. */
101 aliceInit(aliceDataSource
);
102 bobInit(bobDataSource
);
104 /* Start syncing by making alice send the first message. */
105 ok(SOSEngineSyncWithPeer(aliceEngine
, bobPeer
, false, &error
), "tell Alice sync with peer Bob");
114 message
= dequeueMessage();
116 /* We are expecting a message and msg is it's digest. */
118 CFStringRef messageDesc
= SOSMessageCopyDescription(message
);
119 CFStringRef messageDigestStr
= SOSMessageCopyDigestHex(message
);
121 bool handeled
= SOSEngineHandleMessage(alice
? aliceEngine
: bobEngine
, alice
? bobPeer
: alicePeer
, message
, &error
);
122 if (!CFEqual(messageDigestStr
, msg
)) {
124 fail("%s %s received message [%d] digest %@ != %@ %@", name
, alice
? "Alice" : "Bob", msg_index
, messageDigestStr
, msg
, messageDesc
);
126 fail("%s %s failed to handle message [%d] digest %@ != %@ %@: %@", name
, alice
? "Alice" : "Bob", msg_index
, messageDigestStr
, msg
, messageDesc
, error
);
127 CFReleaseNull(error
);
129 } else if (handeled
) {
130 pass("%s %s handled message [%d] %@", name
, alice
? "Alice" : "Bob", msg_index
, messageDesc
);
132 fail("%s %s failed to handle message [%d] %@: %@", name
, alice
? "Alice" : "Bob", msg_index
, messageDesc
, error
);
133 CFReleaseNull(error
);
136 fail("%s %s sent extra message [%d] with digest %@: %@", name
, alice
? "Bob" : "Alice", msg_index
, messageDigestStr
, messageDesc
);
138 CFRelease(messageDigestStr
);
139 CFRelease(messageDesc
);
143 fail("%s %s expected message [%d] with digest %@, none received", name
, alice
? "Alice" : "Bob", msg_index
, msg
);
149 msg
= va_arg(msgs
, CFStringRef
);
156 SOSEngineDispose(aliceEngine
); // Also disposes aliceDataSource
157 SOSPeerDispose(alicePeer
);
158 CFReleaseSafe(aliceID
);
160 SOSEngineDispose(bobEngine
); // Also disposes bobDataSource
161 SOSPeerDispose(bobPeer
);
162 CFReleaseSafe(bobID
);
164 aliceDataSourceFactory
->release(aliceDataSourceFactory
);
168 static SOSObjectRef
SOSDataSourceCopyObject(SOSDataSourceRef ds
, SOSObjectRef match
, CFErrorRef
*error
)
170 __block SOSObjectRef result
= NULL
;
172 CFDataRef digest
= ds
->copyDigest(match
, error
);
173 SOSManifestRef manifest
= NULL
;
175 require(digest
, exit
);
177 manifest
= SOSManifestCreateWithData(digest
, error
);
179 ds
->foreach_object(ds
, manifest
, error
, ^ bool (SOSObjectRef object
, CFErrorRef
*error
) {
180 if (result
== NULL
) {
182 CFRetainSafe(result
);
189 CFReleaseNull(manifest
);
190 CFReleaseNull(digest
);
194 static void synctests(void) {
196 // TODO: Adding items gives us non predictable creation and mod dates so
197 // the message hashes can't be precomputed.
198 CFDictionaryRef item
= CFDictionaryCreateForCFTypes
200 kSecClass
, kSecClassGenericPassword
,
201 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
202 kSecAttrSynchronizable
, kCFBooleanTrue
,
203 kSecAttrService
, CFSTR("service"),
204 kSecAttrAccount
, CFSTR("account"),
206 SecItemAdd(item
, NULL
);
214 // Careful with this in !NO_SERVER, it'll destroy debug keychains.
215 WithPathInKeychainDirectory(CFSTR("keychain-2-debug.db"), ^(const char *keychain_path
) {
216 unlink(keychain_path
);
219 // Don't ever do this in !NO_SERVER, it'll destroy real keychains.
220 WithPathInKeychainDirectory(CFSTR("keychain-2.db"), ^(const char *keychain_path
) {
221 unlink(keychain_path
);
224 void kc_dbhandle_reset(void);
227 skip("Keychain not reset", kTestTestCount
, false);
230 // Sync between 2 empty dataSources
231 testsync("sd_70_engine", test_directive
, test_reason
,
232 ^ (SOSDataSourceRef dataSource
) {},
233 ^ (SOSDataSourceRef dataSource
) {},
234 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"),
235 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"),
236 CFSTR("2AF312E092D67308A0083DFFBF2B6B754B967864"),
239 // Sync a dataSource with one object to an empty dataSource
240 testsync("sd_70_engine-alice1", test_directive
, test_reason
,
241 ^ (SOSDataSourceRef dataSource
) {
242 CFErrorRef error
= NULL
;
243 SOSObjectRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
244 // TODO: Needs to be a SecDBItemRef for the SecItemDataSource...
245 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
246 CFReleaseSafe(object
);
247 CFReleaseNull(error
);
249 ^ (SOSDataSourceRef dataSource
) {},
250 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
251 CFSTR("147B6C509908CC4A9FC4263973A842104A64CE01"),
252 CFSTR("019B494F3C06B48BB02C280AF1E19AD861A7003C"),
253 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
256 // Sync a dataSource with one object to another dataSource with the same object
257 testsync("sd_70_engine-alice1bob1", test_directive
, test_reason
,
258 ^ (SOSDataSourceRef dataSource
) {
260 CFErrorRef error
= NULL
;
261 // TODO: Needs to be a SecDBItemRef for the SecItemDataSource...
262 CFDictionaryRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
263 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
264 CFReleaseSafe(object
);
265 CFReleaseNull(error
);
268 ^ (SOSDataSourceRef dataSource
) {
269 CFErrorRef error
= NULL
;
270 SOSObjectRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
271 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
272 CFReleaseSafe(object
);
273 CFReleaseNull(error
);
275 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
276 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
277 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
280 // Sync a dataSource with one object to another dataSource with the same object
281 testsync("sd_70_engine-alice1bob2", test_directive
, test_reason
,
282 ^ (SOSDataSourceRef dataSource
) {
284 CFErrorRef error
= NULL
;
285 // TODO: Needs to be a SecDBItemRef for the SecItemDataSource...
286 SOSObjectRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
287 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
288 CFReleaseSafe(object
);
289 CFReleaseNull(error
);
292 ^ (SOSDataSourceRef dataSource
) {
293 CFErrorRef error
= NULL
;
294 SOSObjectRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
295 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
296 CFReleaseSafe(object
);
297 object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("account1"), CFSTR("service1"));
298 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
299 CFReleaseSafe(object
);
300 CFReleaseNull(error
);
302 CFSTR("ADAA3ACE75ED516CB91893413EE9CC9ED04CA47B"),
303 CFSTR("D4049A1063CFBF7CAF8424E13DE3CE926FF5856C"),
304 CFSTR("9624EA855BBED6B668868BB723443E804D04F6A1"),
305 CFSTR("063E097CCD4FEB7F3610ED12B3DA828467314846"),
306 CFSTR("D1B3944E3084425F41B2C2EA0BE82170E10AA37D"),
309 // Sync a dataSource with a tombstone object to another dataSource with the same object
311 todo("<rdar://problem/14049022> Test case in sd-70-engine fails due to need for RowID");
312 testsync("sd_70_engine-update", test_directive
, test_reason
,
313 ^ (SOSDataSourceRef dataSource
) {
314 CFErrorRef error
= NULL
;
315 const char *password
= "password1";
316 CFDataRef data
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)password
, strlen(password
));
317 // TODO: Needs to be a SecDBItemRef for the SecItemDataSource...
318 SOSObjectRef object_to_find
= SOSDataSourceCreateGenericItemWithData(dataSource
, CFSTR("test_account"), CFSTR("test service"), true, NULL
);
319 SOSObjectRef object
= SOSDataSourceCopyObject(dataSource
, object_to_find
, &error
);
320 SOSObjectRef old_object
= NULL
;
322 skip("no object", 1, ok(object
, "Finding object %@, error: %@", object_to_find
, error
));
324 // TODO: Needs to be a SecDBItemRef for the SecItemDataSource...
325 old_object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
326 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource update object %@", error
);
329 CFReleaseSafe(old_object
);
330 CFReleaseSafe(object
);
331 CFReleaseNull(error
);
333 ^ (SOSDataSourceRef dataSource
) {
334 CFErrorRef error
= NULL
;
335 SOSObjectRef object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("test_account"), CFSTR("test service"));
336 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
337 CFReleaseSafe(object
);
338 object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("account1"), CFSTR("service1"));
339 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
340 CFReleaseSafe(object
);
341 CFReleaseNull(error
);
343 CFSTR("5D07A221A152D6D6C5F1919189F259A7278A08C5"),
344 CFSTR("D4049A1063CFBF7CAF8424E13DE3CE926FF5856C"),
345 CFSTR("137FD34E9BF11B4BA0620E8EBFAB8576BCCCF294"),
346 CFSTR("5D07A221A152D6D6C5F1919189F259A7278A08C5"),
350 // Sync a dataSource with one object to another dataSource with the same object
351 testsync("sd_70_engine-foreign-add", test_directive
, test_reason
,
352 ^ (SOSDataSourceRef dataSource
) {
354 ^ (SOSDataSourceRef dataSource
) {
355 CFErrorRef error
= NULL
;
356 const char *password
= "password1";
357 CFDataRef data
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)password
, strlen(password
));
358 SOSObjectRef object
= SOSDataSourceCreateGenericItemWithData(dataSource
, CFSTR("test_account"), CFSTR("test service"), false, data
);
360 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
361 CFReleaseSafe(object
);
362 CFReleaseNull(error
);
363 object
= SOSDataSourceCreateGenericItem(dataSource
, CFSTR("account1"), CFSTR("service1"));
364 ok(dataSource
->add(dataSource
, object
, &error
), "dataSource added object %@", error
);
365 CFReleaseSafe(object
);
366 CFReleaseNull(error
);
368 CFSTR("D1B3944E3084425F41B2C2EA0BE82170E10AA37D"),
369 CFSTR("607EEF976943FD781CFD2B3850E6DC7979AA61EF"),
370 CFSTR("28434CD1B90CC205460557CAC03D7F12067F2329"),
371 CFSTR("D1B3944E3084425F41B2C2EA0BE82170E10AA37D"),
376 int sd_70_engine(int argc
, char *const *argv
)
378 plan_tests(kTestTestCount
);