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