]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/Regressions/SOSTestDevice.c
Security-57031.30.12.tar.gz
[apple/security.git] / Security / sec / SOSCircle / Regressions / SOSTestDevice.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 // Test syncing between SecItemDataSource and SOSTestDataSource
26
27 #include "SOSTestDevice.h"
28 #include "SOSTestDataSource.h"
29 #include <test/testmore.h>
30
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>
42
43 #include <stdint.h>
44 #include <AssertMacros.h>
45
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]);
55 }
56 CFReleaseSafe(msgData);
57 return hex;
58 }
59
60 static void SOSTestDeviceDestroy(CFTypeRef cf) {
61 SOSTestDeviceRef td = (SOSTestDeviceRef)cf;
62 CFReleaseSafe(td->peers);
63 if (td->ds)
64 SOSDataSourceRelease(td->ds, NULL);
65 if (td->dsf)
66 td->dsf->release(td->dsf);
67 CFReleaseSafe(td->db);
68 }
69
70 CFStringRef SOSTestDeviceGetID(SOSTestDeviceRef td) {
71 CFStringRef engineID = NULL;
72 SOSEngineRef engine = SOSDataSourceGetSharedEngine(td->ds, NULL);
73 if (engine)
74 engineID = SOSEngineGetMyID(engine);
75 return engineID;
76 }
77
78 void SOSTestDeviceForEachPeerID(SOSTestDeviceRef td, void(^peerBlock)(CFStringRef peerID, bool *stop)) {
79 SOSPeerRef peer;
80 bool stop = false;
81 CFArrayForEachC(td->peers, peer) {
82 peerBlock(SOSPeerGetID(peer), &stop);
83 if (stop)
84 break;
85 }
86 }
87
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);
95 CFReleaseSafe(peer);
96 });
97 CFStringAppendFormat(result, NULL, CFSTR(">"));
98 return result;
99 }
100
101 CFGiblisFor(SOSTestDevice)
102
103 static SOSTestDeviceRef SOSTestDeviceCreateInternal(CFAllocatorRef allocator, CFStringRef engineID) {
104 SOSTestDeviceRef td = CFTypeAllocate(SOSTestDevice, struct __OpaqueSOSTestDevice, allocator);
105 td->peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
106 td->mute = false;
107 return td;
108 }
109
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);
121 }
122 CFReleaseNull(ds_names);
123 assert(td->ds); // Shut up static analyzer and test generally run in debug mode anyway
124 if (td->ds)
125 SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td->ds, NULL), engineID, NULL, NULL);
126 return td;
127 }
128
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);
134 CFReleaseSafe(db);
135 CFReleaseSafe(path);
136 CFReleaseSafe(url);
137 return td;
138 }
139
140 SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID) {
141 setup("create device");
142 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
143
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);
149 return td;
150 }
151
152 SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version) {
153 setup("create device");
154 CFStringRef engineID = SOSTestDeviceGetID(td);
155 CFStringRef peerID;
156 CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
157 CFErrorRef error = NULL;
158 CFArrayForEachC(peerIDs, peerID) {
159 if (isString(peerID) && !CFEqualSafe(peerID, engineID)) {
160 SOSPeerRef peer;
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);
165 }
166 }
167
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);
172 }
173 CFReleaseSafe(trustedPeersIDs);
174 return td;
175 }
176
177 SOSTestDeviceRef SOSTestDeviceSetMute(SOSTestDeviceRef td, bool mute) {
178 td->mute = mute;
179 return td;
180 }
181
182 bool SOSTestDeviceIsMute(SOSTestDeviceRef td) {
183 return td->mute;
184 }
185
186 CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
187 setup("create message");
188 CFErrorRef error = NULL;
189 SOSEnginePeerMessageSentBlock sent = NULL;
190 CFDataRef msgData;
191 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &sent, &error),
192 "create message to %@: %@", peerID, error);
193 if (sent)
194 sent(true);
195
196 return msgData;
197 }
198
199 #if 0
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);
206 if (msg) {
207 CFDictionaryAddValue(messages, peerID, msg);
208 CFRelease(msg);
209 }
210 }
211 return messages;
212 }
213 #endif
214
215 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td, CFStringRef peerID, CFDataRef msgData) {
216 setup("handle message");
217 if (!msgData) return false;
218 CFErrorRef error = NULL;
219 bool handled;
220 SOSMessageRef message;
221
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);
228
229 CFReleaseNull(message);
230 return handled;
231 }
232
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);
240 }))
241 fail("ds transaction %@", error);
242 CFReleaseNull(error);
243 }
244
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);
252 }))
253 fail("ds transaction %@", error);
254 CFReleaseNull(error);
255 }
256
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) {
261 bool success = true;
262 CFIndex ix = 0;
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);
268 }
269 ok(success, "%@ added %" PRIdCFIndex " API objects %@", SOSTestDeviceGetID(td), ix, error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
270 didAdd = success && ix == count;
271 CFReleaseNull(error);
272 }))
273 fail("ds transaction %@", error);
274 CFReleaseNull(error);
275 return didAdd;
276 }
277
278 CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs) {
279 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
280 CFStringRef deviceID;
281 CFArrayForEachC(deviceIDs, deviceID) {
282 SOSTestDeviceRef device;
283 if (!realDb)
284 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID);
285 else
286 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
287 SOSTestDeviceSetPeerIDs(device, deviceIDs, version);
288 CFDictionarySetValue(testDevices, deviceID, device);
289 CFReleaseSafe(device);
290 }
291 CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs);
292 return testDevices;
293 }
294
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;
302 do {
303 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, deviceIX++);
304 if (deviceIX >= CFArrayGetCount(deviceIDs))
305 deviceIX = 0;
306
307 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
308 SOSTestDeviceForEachPeerID(source, ^(CFStringRef destID, bool *stop) {
309 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
310 if (dest) {
311 if (pre) {
312 if (pre(source, dest))
313 msgSentSinceLastChangeCount = 0;
314 }
315 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
316 SOSMessageRef message = NULL;
317 bool handled = false;
318 msgSentSinceLastChangeCount++;
319 if (msg && CFDataGetLength(msg) > 0) {
320 if (!source->mute) {
321 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
322 if (handled) {
323 noMsgSentCount = 0;
324 }
325
326 CFErrorRef error = NULL;
327 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
328 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
329 CFReleaseNull(error);
330 }
331 } else {
332 SOSManifestRef sourceManifest = SOSEngineCopyManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), NULL);
333 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
334 CFReleaseSafe(sourceManifest);
335 noMsgSentCount++;
336 //msgSentSinceLastChangeCount = 0;
337 }
338 CFReleaseSafe(msg);
339 if (post) {
340 if (post(source, dest, message))
341 msgSentSinceLastChangeCount = 0;
342 }
343 CFReleaseNull(message);
344 }
345 if (noMsgSentCount >= edgeCount) {
346 *stop = done = true;
347 } else if (msgSentSinceLastChangeCount >= 9 * edgeCount + 1) {
348 fail("%s %" PRIdCFIndex" peers never stopped syncing %" PRIdCFIndex" messages since last change", name, CFArrayGetCount(deviceIDs), msgSentSinceLastChangeCount);
349 *stop = done = true;
350 }
351 });
352 } while (!done);
353 }
354
355 bool SOSTestDeviceListInSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices) {
356 bool inSync = true;
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);
369 inSync = false;
370 }
371 CFReleaseSafe(manifest);
372 }
373 CFReleaseSafe(sourceManifest);
374 if (inSync)
375 pass("%s all peers in sync", name);
376 return inSync;
377 }
378
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), ...) {
382 va_list args;
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);
391 }