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