]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/Regressions/SOSTestDevice.c
Security-57031.1.35.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 SOSTestDeviceCopyDescription(CFTypeRef cf) {
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 return td;
107 }
108
109 SOSTestDeviceRef SOSTestDeviceCreateWithDb(CFAllocatorRef allocator, CFStringRef engineID, SecDbRef db) {
110 setup("create device");
111 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
112 CFRetainAssign(td->db, db);
113 td->dsf = SecItemDataSourceFactoryGetShared(td->db);
114 CFArrayRef ds_names = td->dsf->copy_names(td->dsf);
115 CFErrorRef error = NULL;
116 if (ds_names && CFArrayGetCount(ds_names) > 0) {
117 CFStringRef sname = CFArrayGetValueAtIndex(ds_names, 0);
118 ok (td->ds = td->dsf->create_datasource(td->dsf, sname, &error), "%@ create datasource \"%@\" [error: %@]", engineID, sname, error);
119 CFReleaseNull(error);
120 }
121 CFReleaseNull(ds_names);
122 assert(td->ds); // Shut up static analyzer and test generally run in debug mode anyway
123 if (td->ds)
124 SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td->ds, NULL), engineID, NULL, NULL);
125 return td;
126 }
127
128 SOSTestDeviceRef SOSTestDeviceCreateWithDbNamed(CFAllocatorRef allocator, CFStringRef engineID, CFStringRef dbName) {
129 CFURLRef url = SecCopyURLForFileInKeychainDirectory(dbName);
130 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
131 SecDbRef db = SecKeychainDbCreate(path);
132 SOSTestDeviceRef td = SOSTestDeviceCreateWithDb(allocator, engineID, db);
133 CFReleaseSafe(db);
134 CFReleaseSafe(path);
135 CFReleaseSafe(url);
136 return td;
137 }
138
139 SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID) {
140 setup("create device");
141 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
142
143 td->ds = SOSTestDataSourceCreate();
144 CFErrorRef error = NULL;
145 ok(td->ds->engine = SOSEngineCreate(td->ds, &error), "create engine: %@", error);
146 SOSEngineCircleChanged(td->ds->engine, engineID, NULL, NULL);
147 CFReleaseNull(error);
148 return td;
149 }
150
151 SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version) {
152 setup("create device");
153 CFStringRef engineID = SOSTestDeviceGetID(td);
154 CFStringRef peerID;
155 CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
156 CFErrorRef error = NULL;
157 CFArrayForEachC(peerIDs, peerID) {
158 if (isString(peerID) && !CFEqualSafe(peerID, engineID)) {
159 SOSPeerRef peer;
160 ok(peer = SOSPeerCreateSimple(td->ds->engine, peerID, version, &error), "create peer: %@", error);
161 CFReleaseNull(error);
162 CFArrayAppendValue(td->peers, peer);
163 CFArrayAppendValue(trustedPeersIDs, peerID);
164 }
165 }
166
167 SOSEngineCircleChanged(td->ds->engine, engineID, trustedPeersIDs, NULL);
168 CFArrayForEachC(trustedPeersIDs, peerID) {
169 ok(SOSEnginePeerDidConnect(td->ds->engine, peerID, &error), "tell %@ %@ connected: %@", engineID, peerID, error);
170 CFReleaseNull(error);
171 }
172 CFReleaseSafe(trustedPeersIDs);
173 return td;
174 }
175
176 CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
177 setup("create message");
178 CFErrorRef error = NULL;
179 SOSEnginePeerMessageSentBlock sent = NULL;
180 CFDataRef msgData;
181 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &sent, &error),
182 "create message to %@: %@", peerID, error);
183 if (sent)
184 sent(true);
185
186 return msgData;
187 }
188
189 #if 0
190 CFDictionaryRef SOSTestDeviceCreateMessages(SOSTestDeviceRef td) {
191 CFTypeRef peer = NULL;
192 CFMutableDictionaryRef messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
193 CFArrayForEachC(td->peers, peer) {
194 CFStringRef peerID = SOSPeerGetID((SOSPeerRef)peer);
195 CFDataRef msg = SOSTestDeviceCreateMessage(td, peerID);
196 if (msg) {
197 CFDictionaryAddValue(messages, peerID, msg);
198 CFRelease(msg);
199 }
200 }
201 return messages;
202 }
203 #endif
204
205 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td, CFStringRef peerID, CFDataRef msgData) {
206 setup("handle message");
207 if (!msgData) return false;
208 CFErrorRef error = NULL;
209 bool handled;
210 SOSMessageRef message;
211
212 ok(message = SOSMessageCreateWithData(kCFAllocatorDefault, msgData, &error), "decode message %@: %@", msgData, error);
213 CFReleaseNull(error);
214 pass("handeling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message);
215 ok(handled = SOSEngineHandleMessage(SOSDataSourceGetSharedEngine(td->ds, &error), peerID, msgData, &error),
216 "handled from %@ %@: %@", peerID, message, error);
217 CFReleaseNull(error);
218
219 CFReleaseNull(message);
220 return handled;
221 }
222
223 void SOSTestDeviceAddGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
224 __block CFErrorRef error = NULL;
225 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
226 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
227 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added API object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
228 CFReleaseSafe(object);
229 CFReleaseNull(error);
230 }))
231 fail("ds transaction %@", error);
232 CFReleaseNull(error);
233 }
234
235 void SOSTestDeviceAddRemoteGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
236 __block CFErrorRef error = NULL;
237 if (!SOSDataSourceWithAPI(td->ds, false, &error, ^(SOSTransactionRef txn, bool *commit) {
238 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
239 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added remote object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
240 CFReleaseSafe(object);
241 CFReleaseNull(error);
242 }))
243 fail("ds transaction %@", error);
244 CFReleaseNull(error);
245 }
246
247 bool SOSTestDeviceAddGenericItems(SOSTestDeviceRef td, CFIndex count, CFStringRef account, CFStringRef server) {
248 __block bool didAdd = false;
249 __block CFErrorRef error = NULL;
250 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
251 bool success = true;
252 CFIndex ix = 0;
253 for (; success && ix < count; ++ix) {
254 CFStringRef accountStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%" PRIdCFIndex), account, ix);
255 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, accountStr, server);
256 success = SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
257 CFReleaseSafe(object);
258 }
259 ok(success, "%@ added %" PRIdCFIndex " API objects %@", SOSTestDeviceGetID(td), ix, error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
260 didAdd = success && ix == count;
261 CFReleaseNull(error);
262 }))
263 fail("ds transaction %@", error);
264 CFReleaseNull(error);
265 return didAdd;
266 }
267
268 CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs) {
269 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
270 CFStringRef deviceID;
271 CFArrayForEachC(deviceIDs, deviceID) {
272 SOSTestDeviceRef device;
273 if (!realDb)
274 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID);
275 else
276 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
277 SOSTestDeviceSetPeerIDs(device, deviceIDs, version);
278 CFDictionarySetValue(testDevices, deviceID, device);
279 CFReleaseSafe(device);
280 }
281 CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs);
282 return testDevices;
283 }
284
285 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)) {
286 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
287 const CFIndex edgeCount = CFArrayGetCount(deviceIDs) * (CFArrayGetCount(deviceIDs) - 1);
288 CFIndex deviceIX = 0;
289 __block CFIndex noMsgSentCount = 0;
290 __block CFIndex msgSentSinceLastChangeCount = 0;
291 __block bool done = false;
292 do {
293 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, deviceIX++);
294 if (deviceIX >= CFArrayGetCount(deviceIDs))
295 deviceIX = 0;
296
297 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
298 SOSTestDeviceForEachPeerID(source, ^(CFStringRef destID, bool *stop) {
299 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
300 if (dest) {
301 if (pre) {
302 if (pre(source, dest))
303 msgSentSinceLastChangeCount = 0;
304 }
305 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
306 SOSMessageRef message = NULL;
307 bool handled = false;
308 msgSentSinceLastChangeCount++;
309 if (msg && CFDataGetLength(msg) > 0) {
310 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
311 if (handled) {
312 noMsgSentCount = 0;
313 }
314
315 CFErrorRef error = NULL;
316 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
317 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
318 CFReleaseNull(error);
319 } else {
320 SOSManifestRef sourceManifest = SOSEngineCopyManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), NULL);
321 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
322 CFReleaseSafe(sourceManifest);
323 noMsgSentCount++;
324 //msgSentSinceLastChangeCount = 0;
325 }
326 CFReleaseSafe(msg);
327 if (post) {
328 if (post(source, dest, message))
329 msgSentSinceLastChangeCount = 0;
330 }
331 CFReleaseNull(message);
332 }
333 if (noMsgSentCount >= edgeCount) {
334 *stop = done = true;
335 } else if (msgSentSinceLastChangeCount >= 9 * edgeCount + 1) {
336 fail("%s %" PRIdCFIndex" peers never stopped syncing %" PRIdCFIndex" messages since last change", name, CFArrayGetCount(deviceIDs), msgSentSinceLastChangeCount);
337 *stop = done = true;
338 }
339 });
340 } while (!done);
341 }
342
343 bool SOSTestDeviceListInSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices) {
344 bool inSync = true;
345 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
346 CFStringRef sourceID = NULL;
347 SOSManifestRef sourceManifest = NULL;
348 CFStringRef currentID;
349 CFArrayForEachC(deviceIDs, currentID) {
350 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, currentID);
351 SOSManifestRef manifest = SOSEngineCopyManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), NULL);
352 if (!sourceManifest) {
353 sourceManifest = CFRetainSafe(manifest);
354 sourceID = currentID;
355 } else if (!CFEqual(manifest, sourceManifest)) {
356 fail("%s %@ manifest %@ != %@ manifest %@", name, currentID, manifest, sourceID, sourceManifest);
357 inSync = false;
358 }
359 CFReleaseSafe(manifest);
360 }
361 CFReleaseSafe(sourceManifest);
362 if (inSync)
363 pass("%s all peers in sync", name);
364 return inSync;
365 }
366
367 void SOSTestDeviceListTestSync(const char *name, const char *test_directive, const char *test_reason, CFIndex version, bool use_db,
368 bool(^pre)(SOSTestDeviceRef source, SOSTestDeviceRef dest),
369 bool(^post)(SOSTestDeviceRef source, SOSTestDeviceRef dest, SOSMessageRef message), ...) {
370 va_list args;
371 va_start(args, post);
372 // Optionally prefix each peer with name to make them more unique.
373 CFArrayRef deviceIDs = CFArrayCreateForVC(kCFAllocatorDefault, &kCFTypeArrayCallBacks, args);
374 CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs);
375 CFReleaseSafe(deviceIDs);
376 SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post);
377 SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices);
378 CFReleaseSafe(testDevices);
379 }