]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/Regressions/SOSAccountTesting.h
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / securityd / Regressions / SOSAccountTesting.h
1 /*
2 * Copyright (c) 2013-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 #ifndef SEC_SOSAccountTesting_h
26 #define SEC_SOSAccountTesting_h
27
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <Security/SecureObjectSync/SOSAccount.h>
30 #include <Security/SecureObjectSync/SOSAccountPriv.h>
31 #include <Security/SecureObjectSync/SOSTransport.h>
32 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
33 #include <Security/SecureObjectSync/SOSPeerInfo.h>
34 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
35
36 #include "SOSTestDataSource.h"
37 #include "SOSRegressionUtilities.h"
38
39 #include "SOSTransportTestTransports.h"
40
41 #include <utilities/SecCFWrappers.h>
42
43 //
44 // Implicit transaction helpers
45 //
46
47 static inline bool SOSAccountResetToOffering_wTxn(SOSAccountRef account, CFErrorRef* error)
48 {
49 __block bool result = false;
50 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
51 result = SOSAccountResetToOffering(txn, error);
52 });
53 return result;
54 }
55
56 static inline bool SOSAccountJoinCirclesAfterRestore_wTxn(SOSAccountRef account, CFErrorRef* error)
57 {
58 __block bool result = false;
59 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
60 result = SOSAccountJoinCirclesAfterRestore(txn, error);
61 });
62 return result;
63 }
64
65 static inline bool SOSAccountJoinCircles_wTxn(SOSAccountRef account, CFErrorRef* error)
66 {
67 __block bool result = false;
68 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
69 result = SOSAccountJoinCircles(txn, error);
70 });
71 return result;
72 }
73
74 static inline bool SOSAccountCheckHasBeenInSync_wTxn(SOSAccountRef account)
75 {
76 return SOSAccountHasCompletedInitialSync(account);
77 }
78
79 static inline void SOSAccountPeerGotInSync_wTxn(SOSAccountRef account, SOSPeerInfoRef peer)
80 {
81 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
82 CFMutableSetRef views = SOSPeerInfoCopyEnabledViews(peer);
83 SOSAccountPeerGotInSync(txn, SOSPeerInfoGetPeerID(peer), views);
84 CFReleaseNull(views);
85 });
86 }
87
88 static inline bool SOSAccountSetBackupPublicKey_wTxn(SOSAccountRef account, CFDataRef backupKey, CFErrorRef* error)
89 {
90 __block bool result = false;
91 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
92 result = SOSAccountSetBackupPublicKey(txn, backupKey, error);
93 });
94 return result;
95 }
96
97 static inline bool SOSAccountRemoveBackupPublickey_wTxn(SOSAccountRef account, CFErrorRef* error)
98 {
99 __block bool result = false;
100 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
101 result = SOSAccountRemoveBackupPublickey(txn, error);
102 });
103 return result;
104 }
105
106 static inline SOSViewResultCode SOSAccountUpdateView_wTxn(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
107 __block SOSViewResultCode result = false;
108 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
109 result = SOSAccountUpdateView(account, viewname, actionCode, error);
110 });
111 return result;
112 }
113
114 static inline bool SOSAccountSetMyDSID_wTxn(SOSAccountRef account, CFStringRef dsid, CFErrorRef* error)
115 {
116 __block bool result = false;
117 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
118 result = SOSAccountSetMyDSID(txn, dsid, error);
119 });
120 return result;
121 }
122
123 //
124 // Account comparison
125 //
126
127 #define kAccountsAgreeTestMin 9
128 #define kAccountsAgreeTestPerPeer 1
129 #define accountsAgree(x) (kAccountsAgreeTestMin + kAccountsAgreeTestPerPeer * (x))
130
131 static void SOSAccountResetToTest(SOSAccountRef a, CFStringRef accountName) {
132 SOSUnregisterTransportKeyParameter(a->key_transport);
133
134 CFReleaseNull(a->circle_transport);
135 CFReleaseNull(a->kvs_message_transport);
136 CFReleaseNull(a->key_transport);
137
138 SOSAccountEnsureFactoryCirclesTest(a, accountName);
139 }
140
141
142 static SOSAccountRef SOSAccountCreateBasicTest(CFAllocatorRef allocator,
143 CFStringRef accountName,
144 CFDictionaryRef gestalt,
145 SOSDataSourceFactoryRef factory) {
146 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
147
148 return a;
149 }
150
151 static SOSAccountRef SOSAccountCreateTest(CFAllocatorRef allocator,
152 CFStringRef accountName,
153 CFDictionaryRef gestalt,
154 SOSDataSourceFactoryRef factory) {
155 SOSAccountRef a = SOSAccountCreateBasicTest(allocator, accountName, gestalt, factory);
156
157 SOSAccountResetToTest(a, accountName);
158
159 return a;
160 }
161
162 static SOSAccountRef SOSAccountCreateTestFromData(CFAllocatorRef allocator,
163 CFDataRef data,
164 CFStringRef accountName,
165 SOSDataSourceFactoryRef factory) {
166 SOSAccountRef a = SOSAccountCreateFromData(allocator, data, factory, NULL);
167 if (!a) {
168 CFDictionaryRef gestalt = SOSCreatePeerGestaltFromName(accountName);
169 a = SOSAccountCreate(allocator, gestalt, factory);
170 CFReleaseNull(gestalt);
171 }
172
173 SOSAccountResetToTest(a, accountName);
174
175 return a;
176 }
177
178
179 static inline bool SOSAccountAssertUserCredentialsAndUpdate(SOSAccountRef account,
180 CFStringRef user_account, CFDataRef user_password,
181 CFErrorRef *error)
182 {
183 bool success = false;
184 success = SOSAccountAssertUserCredentials(account, user_account, user_password, error);
185 require_quiet(success, done);
186
187 success = SOSAccountGenerationSignatureUpdate(account, error);
188
189 done:
190 return success;
191 }
192
193
194
195 static void unretired_peers_is_subset(const char* label, CFArrayRef peers, CFSetRef allowed_peers)
196 {
197 CFArrayForEach(peers, ^(const void *value) {
198 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
199
200 CFErrorRef leftError = NULL;
201 CFErrorRef rightError = NULL;
202
203 ok(SOSPeerInfoIsRetirementTicket(pi) || SOSPeerInfoIsCloudIdentity(pi) || CFSetContainsValue(allowed_peers, pi), "Peer is allowed (%s) Peer: %@, Allowed %@", label, pi, allowed_peers);
204
205 CFReleaseNull(leftError);
206 CFReleaseNull(rightError);
207 });
208 }
209
210 static void accounts_agree_internal(char *label, SOSAccountRef left, SOSAccountRef right, bool check_peers)
211 {
212 CFErrorRef error = NULL;
213 {
214 CFArrayRef leftPeers = SOSAccountCopyActivePeers(left, &error);
215 ok(leftPeers, "Left peers (%@) - %s", error, label);
216 CFReleaseNull(error);
217
218 CFArrayRef rightPeers = SOSAccountCopyActivePeers(right, &error);
219 ok(rightPeers, "Right peers (%@) - %s", error, label);
220 CFReleaseNull(error);
221
222 ok(CFEqual(leftPeers, rightPeers), "Matching peers (%s) Left: %@, Right: %@", label, leftPeers, rightPeers);
223
224 if (check_peers) {
225 CFMutableSetRef allowed_identities = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
226
227 SOSFullPeerInfoRef leftFullPeer = SOSAccountCopyAccountIdentityPeerInfo(left, kCFAllocatorDefault, NULL);
228
229 if (leftFullPeer)
230 CFSetAddValue(allowed_identities, SOSFullPeerInfoGetPeerInfo(leftFullPeer));
231
232 CFReleaseNull(leftFullPeer);
233
234 SOSFullPeerInfoRef rightFullPeer = SOSAccountCopyAccountIdentityPeerInfo(right, kCFAllocatorDefault, NULL);
235
236 if (rightFullPeer)
237 CFSetAddValue(allowed_identities, SOSFullPeerInfoGetPeerInfo(rightFullPeer));
238
239 CFReleaseNull(rightFullPeer);
240
241 unretired_peers_is_subset(label, leftPeers, allowed_identities);
242
243 CFReleaseNull(allowed_identities);
244 }
245
246 CFReleaseNull(leftPeers);
247 CFReleaseNull(rightPeers);
248 }
249 {
250 CFArrayRef leftConcurringPeers = SOSAccountCopyConcurringPeers(left, &error);
251 ok(leftConcurringPeers, "Left peers (%@) - %s", error, label);
252
253 CFArrayRef rightConcurringPeers = SOSAccountCopyConcurringPeers(right, &error);
254 ok(rightConcurringPeers, "Right peers (%@) - %s", error, label);
255
256 ok(CFEqual(leftConcurringPeers, rightConcurringPeers), "Matching concurring peers Left: %@, Right: %@", leftConcurringPeers, rightConcurringPeers);
257
258 CFReleaseNull(leftConcurringPeers);
259 CFReleaseNull(rightConcurringPeers);
260 }
261 {
262 CFArrayRef leftApplicants = SOSAccountCopyApplicants(left, &error);
263 ok(leftApplicants, "Left Applicants (%@) - %s", error, label);
264
265 CFArrayRef rightApplicants = SOSAccountCopyApplicants(right, &error);
266 ok(rightApplicants, "Left Applicants (%@) - %s", error, label);
267
268 ok(CFEqual(leftApplicants, rightApplicants), "Matching applicants (%s) Left: %@, Right: %@", label, leftApplicants, rightApplicants);
269
270 CFReleaseNull(leftApplicants);
271 CFReleaseNull(rightApplicants);
272 }
273 }
274
275 static inline void accounts_agree(char *label, SOSAccountRef left, SOSAccountRef right)
276 {
277 accounts_agree_internal(label, left, right, true);
278 }
279
280
281 //
282 // Change handling
283 //
284
285 static inline CFStringRef CFArrayCopyCompactDescription(CFArrayRef array) {
286 if (!isArray(array))
287 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<Not an array! %@>"), array);
288
289 CFMutableStringRef result = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
290
291 __block CFStringRef separator = CFSTR("");
292 CFArrayForEach(array, ^(const void *value) {
293 CFStringAppendFormat(result, NULL, CFSTR("%@%@"), separator, value);
294 separator = CFSTR(",");
295 });
296
297 CFStringAppend(result, CFSTR("]"));
298
299 CFReleaseSafe(separator);
300
301 return result;
302 }
303
304 static inline CFStringRef SOSAccountCopyName(SOSAccountRef account) {
305 SOSPeerInfoRef pi = SOSAccountGetMyPeerInfo(account);
306
307 return pi ? CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerName(pi)) : CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@"), account);
308 }
309
310 static inline CFStringRef CopyChangesDescription(CFDictionaryRef changes) {
311
312 CFStringRef pendingChanges = CFDictionaryCopyCompactDescription((CFDictionaryRef) CFDictionaryGetValue(changes, kCFNull));
313
314 CFMutableStringRef peerTable = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
315
316 __block CFStringRef separator = CFSTR("");
317
318 CFDictionaryForEach(changes, ^(const void *key, const void *value) {
319 if (CFGetTypeID(key) == SOSAccountGetTypeID()) {
320 CFStringRef accountName = SOSAccountCopyName((SOSAccountRef) key);
321 CFStringRef arrayDescription = CFArrayCopyCompactDescription(value);
322
323 CFStringAppendFormat(peerTable, NULL, CFSTR("%@%@:%@"), separator, accountName, arrayDescription);
324 separator = CFSTR(", ");
325
326 CFReleaseSafe(accountName);
327 CFReleaseSafe(arrayDescription);
328 }
329 });
330
331 CFStringAppend(peerTable, CFSTR("]"));
332
333 CFStringRef result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<TestChanges %@ %@>"), pendingChanges, peerTable);
334 CFReleaseNull(pendingChanges);
335 CFReleaseNull(peerTable);
336
337 return result;
338 };
339
340 static void CFDictionaryOverlayDictionary(CFMutableDictionaryRef target, CFMutableDictionaryRef overlay) {
341 CFMutableSetRef keysToRemove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
342
343 CFDictionaryForEach(overlay, ^(const void *key, const void *value) {
344 const void *current_value = CFDictionaryGetValue(target, key);
345 if (CFEqualSafe(current_value, value) || (isNull(value) && current_value == NULL)) {
346 CFSetAddValue(keysToRemove, key);
347 } else {
348 CFDictionarySetValue(target, key, value);
349 }
350 });
351
352 CFSetForEach(keysToRemove, ^(const void *value) {
353 CFDictionaryRemoveValue(overlay, value);
354 });
355
356 CFReleaseNull(keysToRemove);
357 }
358
359 static void CFArrayAppendKeys(CFMutableArrayRef keys, CFDictionaryRef newKeysToAdd) {
360 CFDictionaryForEach(newKeysToAdd, ^(const void *key, const void *value) {
361 CFArrayAppendValue(keys, key);
362 });
363 }
364
365 static bool AddNewChanges(CFMutableDictionaryRef changesRecord, CFMutableDictionaryRef newKeysAndValues, SOSAccountRef sender)
366 {
367 __block bool changes_added = false;
368 CFMutableDictionaryRef emptyDictionary = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
369 CFDictionaryAddValue(changesRecord, kCFNull, emptyDictionary);
370 CFReleaseNull(emptyDictionary);
371
372 CFDictionaryOverlayDictionary((CFMutableDictionaryRef) CFDictionaryGetValue(changesRecord, kCFNull), newKeysAndValues);
373
374 CFDictionaryForEach(changesRecord, ^(const void *key, const void *value) {
375 if (isArray(value) && (sender == NULL || sender != key)) {
376 CFArrayAppendKeys((CFMutableArrayRef) value, newKeysAndValues);
377 if (CFDictionaryGetCount(newKeysAndValues))
378 changes_added = true;
379 }
380 });
381
382 if (changes_added)
383 secnotice("changes", "Changes from %@: %@", sender, newKeysAndValues);
384
385 CFDictionaryRemoveAllValues(newKeysAndValues);
386
387 return changes_added;
388 }
389
390 static bool FillAllChanges(CFMutableDictionaryRef changes) {
391 __block bool changed = false;
392
393 CFMutableSetRef changedAccounts = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
394
395 CFArrayForEach(key_transports, ^(const void *value) {
396 SOSTransportKeyParameterTestRef tpt = (SOSTransportKeyParameterTestRef) value;
397 if (AddNewChanges(changes, SOSTransportKeyParameterTestGetChanges(tpt), SOSTransportKeyParameterTestGetAccount(tpt))) {
398 changed |= true;
399 CFSetAddValue(changedAccounts, SOSTransportKeyParameterTestGetAccount(tpt));
400 }
401 SOSTransportKeyParameterTestClearChanges(tpt);
402 });
403 CFArrayForEach(circle_transports, ^(const void *value) {
404 SOSTransportCircleTestRef tpt = (SOSTransportCircleTestRef) value;
405 if (AddNewChanges(changes, SOSTransportCircleTestGetChanges(tpt), SOSTransportCircleTestGetAccount(tpt))) {
406 changed |= true;
407 CFSetAddValue(changedAccounts, SOSTransportCircleTestGetAccount(tpt));
408 }
409 SOSTransportCircleTestClearChanges(tpt);
410 });
411 CFArrayForEach(message_transports, ^(const void *value) {
412 if(SOSTransportMessageGetTransportType((SOSTransportMessageRef)value, NULL) == kKVSTest){
413 SOSTransportMessageTestRef tpt = (SOSTransportMessageTestRef) value;
414 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt), kCFNull);
415 if (AddNewChanges(changes, SOSTransportMessageTestGetChanges(tpt), SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt))) {
416 changed |= true;
417 CFSetAddValue(changedAccounts, SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt));
418 }
419 SOSTransportMessageTestClearChanges(tpt);
420 }
421 else if(SOSTransportMessageGetTransportType((SOSTransportMessageRef)value, NULL) == kIDSTest){
422 SOSTransportMessageRef ids = (SOSTransportMessageRef) value;
423 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(ids), kCFNull);
424 if (AddNewChanges(changes, SOSTransportMessageIDSTestGetChanges(ids), SOSTransportMessageTestGetAccount(ids))) {
425 changed |= true;
426 CFSetAddValue(changedAccounts, SOSTransportMessageTestGetAccount(ids));
427 }
428 SOSTransportMessageIDSTestClearChanges(ids);
429 }
430 });
431
432 secnotice("process-changes", "Accounts with change (%@): %@", changed ? CFSTR("YES") : CFSTR("NO"), changedAccounts);
433
434 CFReleaseNull(changedAccounts);
435
436 return changed;
437 }
438
439 static void FillChanges(CFMutableDictionaryRef changes, SOSAccountRef forAccount)
440 {
441 CFArrayForEach(key_transports, ^(const void *value) {
442 SOSTransportKeyParameterTestRef tpt = (SOSTransportKeyParameterTestRef) value;
443 if(CFEqualSafe(forAccount, SOSTransportKeyParameterTestGetAccount(tpt))){
444 AddNewChanges(changes, SOSTransportKeyParameterTestGetChanges(tpt), SOSTransportKeyParameterTestGetAccount(tpt));
445 SOSTransportKeyParameterTestClearChanges(tpt);
446 }
447 });
448 CFArrayForEach(circle_transports, ^(const void *value) {
449 SOSTransportCircleTestRef tpt = (SOSTransportCircleTestRef) value;
450 if(CFEqualSafe(forAccount, SOSTransportCircleTestGetAccount(tpt))){
451 AddNewChanges(changes, SOSTransportCircleTestGetChanges(tpt), SOSTransportCircleTestGetAccount(tpt));
452 SOSTransportCircleTestClearChanges(tpt);
453 }
454 });
455 CFArrayForEach(message_transports, ^(const void *value) {
456 if(SOSTransportMessageGetTransportType((SOSTransportMessageRef)value, NULL) == kKVSTest){
457 SOSTransportMessageTestRef tpt = (SOSTransportMessageTestRef) value;
458 if(CFEqualSafe(forAccount, SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt))){
459 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt), kCFNull);
460 AddNewChanges(changes, SOSTransportMessageTestGetChanges(tpt), SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt));
461 SOSTransportMessageTestClearChanges(tpt);
462 }
463 }
464 else{
465 SOSTransportMessageRef tpt = (SOSTransportMessageRef) value;
466 if(CFEqualSafe(forAccount, SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt))){
467 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(tpt), kCFNull);
468 AddNewChanges(changes, SOSTransportMessageIDSTestGetChanges(tpt), SOSTransportMessageTestGetAccount((SOSTransportMessageRef)tpt));
469 SOSTransportMessageIDSTestClearChanges(tpt);
470 }
471 }
472 });
473
474 }
475
476 static inline void FillChangesMulti(CFMutableDictionaryRef changes, SOSAccountRef account, ...)
477 {
478 SOSAccountRef next_account = account;
479 va_list argp;
480 va_start(argp, account);
481 while(next_account != NULL) {
482 FillChanges(changes, next_account);
483 next_account = va_arg(argp, SOSAccountRef);
484 }
485 }
486
487 static inline CFMutableArrayRef CFDictionaryCopyKeys(CFDictionaryRef dictionary)
488 {
489 CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
490
491 CFArrayAppendKeys(result, dictionary);
492
493 return result;
494 }
495
496 #define kFeedChangesToTestCount 1
497 static inline void FeedChangesTo(CFMutableDictionaryRef changes, SOSAccountRef account)
498 {
499 CFDictionaryRef full_list = (CFDictionaryRef) CFDictionaryGetValue(changes, kCFNull);
500
501 if (!isDictionary(full_list))
502 return; // Nothing recorded to send!
503
504 CFMutableArrayRef account_pending_keys = (CFMutableArrayRef)CFDictionaryGetValue(changes, account);
505
506 if (!isArray(account_pending_keys)) {
507 account_pending_keys = CFDictionaryCopyKeys(full_list);
508 CFDictionaryAddValue(changes, account, account_pending_keys);
509 CFReleaseSafe(account_pending_keys); // The dictionary keeps it, we don't retain it here.
510 }
511
512 CFMutableDictionaryRef account_pending_messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
513 CFArrayForEach(account_pending_keys, ^(const void *value) {
514 CFDictionaryAddValue(account_pending_messages, value, CFDictionaryGetValue(full_list, value));
515 });
516
517 secnotice("changes", "Changes for %@:", SOSTransportKeyParameterTestGetName((SOSTransportKeyParameterTestRef) account->key_transport));
518
519 CFDictionaryForEach(account_pending_messages, ^(const void *key, const void *value) {
520 secnotice("changes", " %@", key);
521 });
522
523 __block CFMutableArrayRef handled = NULL;
524 SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
525 __block CFErrorRef error = NULL;
526 ok(handled = SOSTransportDispatchMessages(txn, account_pending_messages, &error), "SOSTransportHandleMessages failed (%@)", error);
527 CFReleaseNull(error);
528 });
529
530 if (isArray(handled)) {
531 CFArrayForEach(handled, ^(const void *value) {
532 CFArrayRemoveAllValue(account_pending_keys, value);
533 });
534 }
535 CFReleaseNull(account_pending_messages);
536 CFReleaseNull(handled);
537 }
538
539 #define kFeedChangesToMultieTestCountPer 1
540
541 static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes, va_list argp)
542 {
543 SOSAccountRef account = NULL;
544 while((account = va_arg(argp, SOSAccountRef)) != NULL) {
545 FeedChangesTo(changes, account);
546 }
547 }
548
549 static inline void FeedChangesToMulti(CFMutableDictionaryRef changes, ...)
550 {
551 va_list argp;
552 va_start(argp, changes);
553
554 FeedChangesToMultiV(changes, argp);
555
556 va_end(argp);
557 }
558
559 static inline void InjectChangeToMulti(CFMutableDictionaryRef changes,
560 CFStringRef changeKey, CFTypeRef changeValue, ...)
561 {
562 CFMutableDictionaryRef changes_to_send = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
563 changeKey, changeValue,
564 NULL);
565 AddNewChanges(changes, changes_to_send, NULL);
566 CFReleaseNull(changes_to_send);
567
568 va_list argp;
569 va_start(argp, changeValue);
570 FeedChangesToMultiV(changes, argp);
571 va_end(argp);
572 }
573
574 static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes, va_list argp)
575 {
576 bool result = FillAllChanges(changes);
577
578 FeedChangesToMultiV(changes, argp);
579
580 return result;
581 }
582
583
584 static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes, ...)
585 {
586 va_list argp;
587 va_start(argp, changes);
588
589 bool result = ProcessChangesOnceV(changes, argp);
590
591 va_end(argp);
592
593 return result;
594 }
595
596 static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes, ...)
597 {
598 va_list argp;
599 va_start(argp, changes);
600
601 int result = 0;
602 bool new_data = false;
603 do {
604 va_list argp_copy;
605 va_copy(argp_copy, argp);
606
607 new_data = ProcessChangesOnceV(changes, argp_copy);
608
609 ++result;
610
611 va_end(argp_copy);
612 } while (new_data);
613
614 va_end(argp);
615
616 return result;
617
618 }
619
620 //
621 // MARK: Account creation
622 //
623
624 static CFStringRef modelFromType(SOSPeerInfoDeviceClass cls) {
625 switch(cls) {
626 case SOSPeerInfo_macOS: return CFSTR("Mac Pro");
627 case SOSPeerInfo_iOS: return CFSTR("iPhone");
628 case SOSPeerInfo_iCloud: return CFSTR("iCloud");
629 case SOSPeerInfo_watchOS: return CFSTR("needWatchOSDeviceName");
630 case SOSPeerInfo_tvOS: return CFSTR("needTVOSDeviceName");
631 default: return CFSTR("GENERICOSTHING");
632 }
633 }
634
635 static inline SOSAccountRef CreateAccountForLocalChangesWithStartingAttributes(CFStringRef name, CFStringRef data_source_name, SOSPeerInfoDeviceClass devclass, CFStringRef serial, CFBooleanRef preferIDS, CFBooleanRef preferIDSFragmentation, CFBooleanRef preferIDSACKModel, CFStringRef transportType, CFStringRef deviceID) {
636
637 SOSDataSourceFactoryRef factory = SOSTestDataSourceFactoryCreate();
638 SOSDataSourceRef ds = SOSTestDataSourceCreate();
639 SOSTestDataSourceFactorySetDataSource(factory, data_source_name, ds);
640 SOSEngineRef engine = SOSEngineCreate(ds, NULL);
641 ds->engine = engine;
642
643 CFMutableDictionaryRef gestalt = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
644 CFDictionaryAddValue(gestalt, kPIUserDefinedDeviceNameKey, name);
645 CFDictionaryAddValue(gestalt, kPIDeviceModelNameKey, modelFromType(devclass));
646 CFDictionaryAddValue(gestalt, kPIOSVersionKey, CFSTR("TESTRUN"));
647
648 CFMutableDictionaryRef testV2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
649 CFDictionaryAddValue(testV2dict, sSerialNumberKey, serial);
650 CFDictionaryAddValue(testV2dict, sPreferIDS, preferIDS);
651 CFDictionaryAddValue(testV2dict, sPreferIDSFragmentation, preferIDSFragmentation);
652 CFDictionaryAddValue(testV2dict, sPreferIDSACKModel, preferIDSACKModel);
653 CFDictionaryAddValue(testV2dict, sTransportType, transportType);
654 CFDictionaryAddValue(testV2dict, sDeviceID, deviceID);
655 SOSAccountRef result = SOSAccountCreateTest(kCFAllocatorDefault, name, gestalt, factory);
656 SOSAccountUpdateV2Dictionary(result, testV2dict);
657
658 CFReleaseSafe(SOSAccountCopyUUID(result));
659
660 CFReleaseNull(gestalt);
661 CFReleaseNull(testV2dict);
662
663 return result;
664 }
665
666 static CFStringRef sGestaltTest = CFSTR("GestaltTest");
667 static CFStringRef sV2Test = CFSTR("V2Test");
668 static inline CFDictionaryRef SOSTestSaveStaticAccountState(SOSAccountRef account) {
669 CFMutableDictionaryRef retval = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
670 CFDictionaryRef gestalt = SOSAccountCopyGestalt(account);
671 CFDictionaryRef v2dictionary = SOSAccountCopyV2Dictionary(account);
672 CFDictionaryAddValue(retval, sGestaltTest, gestalt);
673 CFDictionaryAddValue(retval, sV2Test, v2dictionary);
674 CFReleaseNull(gestalt);
675 CFReleaseNull(v2dictionary);
676 return retval;
677 }
678
679 static inline void SOSTestRestoreAccountState(SOSAccountRef account, CFDictionaryRef saved) {
680 SOSAccountUpdateGestalt(account, CFDictionaryGetValue(saved, sGestaltTest));
681 SOSAccountUpdateV2Dictionary(account, CFDictionaryGetValue(saved, sV2Test));
682 }
683
684 static CFStringRef CFStringCreateRandomHexWithLength(size_t len) {
685 if(len%2) len++;
686 CFDataRef data = CFDataCreateWithRandomBytes(len/2);
687 CFMutableStringRef retval = CFStringCreateMutable(kCFAllocatorDefault, len);
688 CFStringAppendHexData(retval, data);
689 CFReleaseNull(data);
690 return retval;
691 }
692
693 static inline SOSAccountRef CreateAccountForLocalChanges(CFStringRef name, CFStringRef data_source_name)
694 {
695 CFStringRef randomSerial = CFStringCreateRandomHexWithLength(8);
696 CFStringRef randomDevID = CFStringCreateRandomHexWithLength(16);
697 SOSAccountRef retval = CreateAccountForLocalChangesWithStartingAttributes(name, data_source_name, SOSPeerInfo_iOS, randomSerial,
698 kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue, SOSTransportMessageTypeIDSV2, randomDevID);
699 CFReleaseNull(randomSerial);
700 CFReleaseNull(randomDevID);
701 return retval;
702 }
703
704 static inline SOSAccountRef CreateAccountForLocalChangesFromData(CFDataRef flattenedData, CFStringRef name, CFStringRef data_source_name)
705 {
706 SOSDataSourceFactoryRef factory = SOSTestDataSourceFactoryCreate();
707 SOSDataSourceRef ds = SOSTestDataSourceCreate();
708 SOSTestDataSourceFactorySetDataSource(factory, data_source_name, ds);
709 SOSEngineRef engine = SOSEngineCreate(ds, NULL);
710 ds->engine = engine;
711
712 SOSAccountRef result = SOSAccountCreateTestFromData(kCFAllocatorDefault, flattenedData, name, factory);
713
714 return result;
715 }
716
717
718
719 static inline int countPeers(SOSAccountRef account) {
720 CFErrorRef error = NULL;
721 CFArrayRef peers;
722
723 peers = SOSAccountCopyPeers(account, &error);
724 int retval = (int) CFArrayGetCount(peers);
725 CFReleaseNull(error);
726 CFReleaseNull(peers);
727 return retval;
728 }
729
730 static inline int countActivePeers(SOSAccountRef account) {
731 CFErrorRef error = NULL;
732 CFArrayRef peers;
733
734 peers = SOSAccountCopyActivePeers(account, &error);
735 int retval = (int) CFArrayGetCount(peers);
736 CFReleaseNull(error);
737 CFReleaseNull(peers);
738 return retval;
739 }
740
741 static inline int countActiveValidPeers(SOSAccountRef account) {
742 CFErrorRef error = NULL;
743 CFArrayRef peers;
744
745 peers = SOSAccountCopyActiveValidPeers(account, &error);
746 int retval = (int) CFArrayGetCount(peers);
747 CFReleaseNull(error);
748 CFReleaseNull(peers);
749 return retval;
750 }
751
752 static inline int countApplicants(SOSAccountRef account) {
753 CFErrorRef error = NULL;
754 CFArrayRef applicants = SOSAccountCopyApplicants(account, &error);
755 int retval = 0;
756
757 if(applicants) retval = (int)CFArrayGetCount(applicants);
758 CFReleaseNull(error);
759 CFReleaseNull(applicants);
760 return retval;
761 }
762
763
764 static inline void showActiveValidPeers(SOSAccountRef account) {
765 CFErrorRef error = NULL;
766 CFArrayRef peers;
767
768 peers = SOSAccountCopyActiveValidPeers(account, &error);
769 CFArrayForEach(peers, ^(const void *value) {
770 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
771 ok(0, "Active Valid Peer %@", pi);
772 });
773 CFReleaseNull(peers);
774 }
775
776 #define ok_or_quit(COND,MESSAGE,LABEL) ok(COND, MESSAGE); if(!(COND)) goto LABEL
777
778 static inline bool testAccountPersistence(SOSAccountRef account) {
779 SOSDataSourceFactoryRef test_factory = SOSTestDataSourceFactoryCreate();
780 SOSDataSourceRef test_source = SOSTestDataSourceCreate();
781 SOSTestDataSourceFactorySetDataSource(test_factory, CFSTR("TestType"), test_source);
782 CFErrorRef error = NULL;
783 bool retval = false;
784 SOSAccountRef reinflatedAccount = NULL;
785 CFDataRef accountDER = NULL;
786
787 SOSAccountCheckHasBeenInSync_wTxn(account);
788
789 // DER encode account to accountData - this allows checking discreet DER functions
790 size_t size = SOSAccountGetDEREncodedSize(account, &error);
791 CFReleaseNull(error);
792 uint8_t buffer[size];
793 uint8_t* start = SOSAccountEncodeToDER(account, &error, buffer, buffer + sizeof(buffer));
794 CFReleaseNull(error);
795
796 ok_or_quit(start, "successful encoding", errOut);
797 ok_or_quit(start == buffer, "Used whole buffer", errOut);
798
799 accountDER = CFDataCreate(kCFAllocatorDefault, buffer, size);
800 ok_or_quit(accountDER, "Made CFData for Account", errOut);
801
802
803 // Re-inflate to "inflated"
804 reinflatedAccount = SOSAccountCreateFromData(kCFAllocatorDefault, accountDER, test_factory, &error);
805 CFReleaseNull(error);
806 CFReleaseNull(accountDER);
807
808 ok(reinflatedAccount, "inflated");
809 ok(CFEqualSafe(reinflatedAccount, account), "Compares");
810
811 // Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
812 accountDER = SOSAccountCopyEncodedData(reinflatedAccount, kCFAllocatorDefault, &error);
813 CFReleaseNull(error);
814 CFReleaseNull(reinflatedAccount);
815 reinflatedAccount = SOSAccountCreateFromData(kCFAllocatorDefault, accountDER, test_factory, &error);
816 ok(reinflatedAccount, "inflated2");
817 ok(CFEqual(account, reinflatedAccount), "Compares");
818
819 retval = true;
820 errOut:
821 CFReleaseNull(reinflatedAccount);
822 CFReleaseNull(accountDER);
823 return retval;
824 }
825
826 static inline bool SOSTestStartCircleWithAccount(SOSAccountRef account, CFMutableDictionaryRef changes, CFStringRef cfaccount, CFDataRef cfpassword) {
827 bool retval = false;
828 require(SOSAccountAssertUserCredentialsAndUpdate(account, cfaccount, cfpassword, NULL), retOut);
829 is(ProcessChangesUntilNoChange(changes, account, NULL), 1, "updates");
830 require(SOSAccountResetToOffering_wTxn(account, NULL), retOut);
831 is(ProcessChangesUntilNoChange(changes, account, NULL), 1, "updates");
832 retval = true;
833 retOut:
834 return retval;
835 }
836
837 static inline bool SOSTestApproveRequest(SOSAccountRef approver, CFIndex napplicants) {
838 bool retval = false;
839 CFErrorRef error = NULL;
840 CFArrayRef applicants = SOSAccountCopyApplicants(approver, &error);
841
842 ok(applicants && CFArrayGetCount(applicants) == napplicants, "See %ld applicant(s) %@ (%@)", napplicants, applicants, error);
843 CFStringRef approvername = SOSAccountCopyName(approver);
844 ok((retval = SOSAccountAcceptApplicants(approver, applicants, &error)), "%@ accepts (%@)", approvername, error);
845 CFReleaseNull(error);
846 CFReleaseNull(applicants);
847 CFReleaseNull(approvername);
848
849 return retval;
850 }
851
852 #define DROP_USERKEY true
853 #define KEEP_USERKEY false
854
855 static inline bool SOSTestJoinWith(CFDataRef cfpassword, CFStringRef cfaccount, CFMutableDictionaryRef changes, SOSAccountRef joiner) {
856 CFErrorRef error = NULL;
857 // retval will return op failures, not count failures - we'll still report those from in here.
858 bool retval = false;
859
860 FeedChangesTo(changes, joiner);
861
862 ok(SOSAccountAssertUserCredentialsAndUpdate(joiner, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
863 CFReleaseNull(error);
864
865 ProcessChangesUntilNoChange(changes, joiner, NULL);
866
867 ok(retval = SOSAccountJoinCircles_wTxn(joiner, &error), "Applying (%@)", error);
868 CFReleaseNull(error);
869 return retval;
870 }
871
872 static inline bool SOSTestJoinWithApproval(CFDataRef cfpassword, CFStringRef cfaccount, CFMutableDictionaryRef changes, SOSAccountRef approver, SOSAccountRef joiner, bool dropUserKey, int expectedCount, bool expectCleanup) {
873 //CFErrorRef error = NULL;
874 // retval will return op failures, not count failures - we'll still report those from in here.
875 bool retval = false;
876
877 ok(retval = SOSTestJoinWith(cfpassword, cfaccount, changes, joiner), "Applyication Made");
878
879 is(ProcessChangesUntilNoChange(changes, approver, joiner, NULL), 2, "updates");
880
881 int nrounds = 2;
882 if(dropUserKey) SOSAccountPurgePrivateCredential(joiner); // lose the userKey so we don't "fix" the ghost problem yet.
883 else nrounds = 3;
884
885 if(expectCleanup) nrounds++;
886
887 ok(retval &= SOSTestApproveRequest(approver, 1), "Accepting Request to Join");
888 is(ProcessChangesUntilNoChange(changes, approver, joiner, NULL), nrounds, "updates");
889
890 accounts_agree_internal("Successful join shows same circle view", joiner, approver, false);
891 is(countPeers(joiner), expectedCount, "There should be %d valid peers", expectedCount);
892 return retval;
893 }
894
895
896 static inline SOSAccountRef SOSTestCreateAccountAsSerialClone(CFStringRef name, SOSPeerInfoDeviceClass devClass, CFStringRef serial, CFStringRef idsID) {
897 return CreateAccountForLocalChangesWithStartingAttributes(name, CFSTR("TestSource"), devClass, serial, kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue, SOSTransportMessageTypeIDSV2, idsID);
898 }
899
900 static inline bool SOSTestMakeGhostInCircle(CFStringRef name, SOSPeerInfoDeviceClass devClass, CFStringRef serial, CFStringRef idsID,
901 CFDataRef cfpassword, CFStringRef cfaccount, CFMutableDictionaryRef changes,
902 SOSAccountRef approver, int expectedCount) {
903 bool retval = false;
904 SOSAccountRef ghostAccount = SOSTestCreateAccountAsSerialClone(name, devClass, serial, idsID);
905 ok(ghostAccount, "Created Ghost Account");
906 require_quiet(ghostAccount, retOut);
907 if(!ghostAccount) return false;
908 ok(retval = SOSTestJoinWithApproval(cfpassword, cfaccount, changes, approver, ghostAccount, DROP_USERKEY, expectedCount, true), "Ghost Joined Circle with expected result");
909 CFReleaseNull(ghostAccount);
910 retOut:
911 return retval;
912 }
913
914 static inline bool SOSTestChangeAccountDeviceName(SOSAccountRef account, CFStringRef name) {
915 bool retval = false;
916 CFMutableDictionaryRef mygestalt = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerGetGestalt(SOSAccountGetMyPeerInfo(account)));
917 require_quiet(mygestalt, retOut);
918 CFDictionarySetValue(mygestalt, kPIUserDefinedDeviceNameKey, name);
919 retval = SOSAccountUpdateGestalt(account, mygestalt);
920 retOut:
921 CFReleaseNull(mygestalt);
922 return retval;
923 }
924
925 static inline void SOSTestCleanup() {
926 SOSUnregisterAllTransportMessages();
927 SOSUnregisterAllTransportCircles();
928 SOSUnregisterAllTransportKeyParameters();
929 CFArrayRemoveAllValues(key_transports);
930 CFArrayRemoveAllValues(circle_transports);
931 CFArrayRemoveAllValues(message_transports);
932 }
933
934
935 #endif