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