2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #ifndef SEC_SOSAccountTesting_h
26 #define SEC_SOSAccountTesting_h
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <Security/SecureObjectSync/SOSAccount.h>
30 #include <Security/SecureObjectSync/SOSAccountPriv.h>
31 #include <Security/SecureObjectSync/SOSTransport.h>
32 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
33 #include <Security/SecureObjectSync/SOSPeerInfo.h>
34 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
36 #include "SOSTestDataSource.h"
37 #include "SOSRegressionUtilities.h"
39 #include "SOSTransportTestTransports.h"
41 #include <utilities/SecCFWrappers.h>
44 // Implicit transaction helpers
47 static inline bool SOSAccountResetToOffering_wTxn(SOSAccountRef account
, CFErrorRef
* error
)
49 __block
bool result
= false;
50 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
51 result
= SOSAccountResetToOffering(txn
, error
);
56 static inline bool SOSAccountJoinCirclesAfterRestore_wTxn(SOSAccountRef account
, CFErrorRef
* error
)
58 __block
bool result
= false;
59 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
60 result
= SOSAccountJoinCirclesAfterRestore(txn
, error
);
65 static inline bool SOSAccountJoinCircles_wTxn(SOSAccountRef account
, CFErrorRef
* error
)
67 __block
bool result
= false;
68 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
69 result
= SOSAccountJoinCircles(txn
, error
);
74 static inline bool SOSAccountCheckHasBeenInSync_wTxn(SOSAccountRef account
)
76 return SOSAccountHasCompletedInitialSync(account
);
79 static inline void SOSAccountPeerGotInSync_wTxn(SOSAccountRef account
, SOSPeerInfoRef peer
)
81 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
82 CFMutableSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
83 SOSAccountPeerGotInSync(txn
, SOSPeerInfoGetPeerID(peer
), views
);
88 static inline bool SOSAccountSetBackupPublicKey_wTxn(SOSAccountRef account
, CFDataRef backupKey
, CFErrorRef
* error
)
90 __block
bool result
= false;
91 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
92 result
= SOSAccountSetBackupPublicKey(txn
, backupKey
, error
);
97 static inline bool SOSAccountRemoveBackupPublickey_wTxn(SOSAccountRef account
, CFErrorRef
* error
)
99 __block
bool result
= false;
100 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
101 result
= SOSAccountRemoveBackupPublickey(txn
, error
);
106 static inline SOSViewResultCode
SOSAccountUpdateView_wTxn(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
107 __block SOSViewResultCode result
= false;
108 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
109 result
= SOSAccountUpdateView(account
, viewname
, actionCode
, error
);
114 static inline bool SOSAccountSetMyDSID_wTxn(SOSAccountRef account
, CFStringRef dsid
, CFErrorRef
* error
)
116 __block
bool result
= false;
117 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
118 result
= SOSAccountSetMyDSID(txn
, dsid
, error
);
124 // Account comparison
127 #define kAccountsAgreeTestMin 9
128 #define kAccountsAgreeTestPerPeer 1
129 #define accountsAgree(x) (kAccountsAgreeTestMin + kAccountsAgreeTestPerPeer * (x))
131 static void SOSAccountResetToTest(SOSAccountRef a
, CFStringRef accountName
) {
132 SOSUnregisterTransportKeyParameter(a
->key_transport
);
134 CFReleaseNull(a
->circle_transport
);
135 CFReleaseNull(a
->kvs_message_transport
);
136 CFReleaseNull(a
->key_transport
);
138 SOSAccountEnsureFactoryCirclesTest(a
, accountName
);
142 static SOSAccountRef
SOSAccountCreateBasicTest(CFAllocatorRef allocator
,
143 CFStringRef accountName
,
144 CFDictionaryRef gestalt
,
145 SOSDataSourceFactoryRef factory
) {
146 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
151 static SOSAccountRef
SOSAccountCreateTest(CFAllocatorRef allocator
,
152 CFStringRef accountName
,
153 CFDictionaryRef gestalt
,
154 SOSDataSourceFactoryRef factory
) {
155 SOSAccountRef a
= SOSAccountCreateBasicTest(allocator
, accountName
, gestalt
, factory
);
157 SOSAccountResetToTest(a
, accountName
);
162 static SOSAccountRef
SOSAccountCreateTestFromData(CFAllocatorRef allocator
,
164 CFStringRef accountName
,
165 SOSDataSourceFactoryRef factory
) {
166 SOSAccountRef a
= SOSAccountCreateFromData(allocator
, data
, factory
, NULL
);
168 CFDictionaryRef gestalt
= SOSCreatePeerGestaltFromName(accountName
);
169 a
= SOSAccountCreate(allocator
, gestalt
, factory
);
170 CFReleaseNull(gestalt
);
173 SOSAccountResetToTest(a
, accountName
);
179 static inline bool SOSAccountAssertUserCredentialsAndUpdate(SOSAccountRef account
,
180 CFStringRef user_account
, CFDataRef user_password
,
183 bool success
= false;
184 success
= SOSAccountAssertUserCredentials(account
, user_account
, user_password
, error
);
185 require_quiet(success
, done
);
187 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
195 static void unretired_peers_is_subset(const char* label
, CFArrayRef peers
, CFSetRef allowed_peers
)
197 CFArrayForEach(peers
, ^(const void *value
) {
198 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
200 CFErrorRef leftError
= NULL
;
201 CFErrorRef rightError
= NULL
;
203 ok(SOSPeerInfoIsRetirementTicket(pi
) || SOSPeerInfoIsCloudIdentity(pi
) || CFSetContainsValue(allowed_peers
, pi
), "Peer is allowed (%s) Peer: %@, Allowed %@", label
, pi
, allowed_peers
);
205 CFReleaseNull(leftError
);
206 CFReleaseNull(rightError
);
210 static void accounts_agree_internal(char *label
, SOSAccountRef left
, SOSAccountRef right
, bool check_peers
)
212 CFErrorRef error
= NULL
;
214 CFArrayRef leftPeers
= SOSAccountCopyActivePeers(left
, &error
);
215 ok(leftPeers
, "Left peers (%@) - %s", error
, label
);
216 CFReleaseNull(error
);
218 CFArrayRef rightPeers
= SOSAccountCopyActivePeers(right
, &error
);
219 ok(rightPeers
, "Right peers (%@) - %s", error
, label
);
220 CFReleaseNull(error
);
222 ok(CFEqual(leftPeers
, rightPeers
), "Matching peers (%s) Left: %@, Right: %@", label
, leftPeers
, rightPeers
);
225 CFMutableSetRef allowed_identities
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
227 SOSFullPeerInfoRef leftFullPeer
= SOSAccountCopyAccountIdentityPeerInfo(left
, kCFAllocatorDefault
, NULL
);
230 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(leftFullPeer
));
232 CFReleaseNull(leftFullPeer
);
234 SOSFullPeerInfoRef rightFullPeer
= SOSAccountCopyAccountIdentityPeerInfo(right
, kCFAllocatorDefault
, NULL
);
237 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(rightFullPeer
));
239 CFReleaseNull(rightFullPeer
);
241 unretired_peers_is_subset(label
, leftPeers
, allowed_identities
);
243 CFReleaseNull(allowed_identities
);
246 CFReleaseNull(leftPeers
);
247 CFReleaseNull(rightPeers
);
250 CFArrayRef leftConcurringPeers
= SOSAccountCopyConcurringPeers(left
, &error
);
251 ok(leftConcurringPeers
, "Left peers (%@) - %s", error
, label
);
253 CFArrayRef rightConcurringPeers
= SOSAccountCopyConcurringPeers(right
, &error
);
254 ok(rightConcurringPeers
, "Right peers (%@) - %s", error
, label
);
256 ok(CFEqual(leftConcurringPeers
, rightConcurringPeers
), "Matching concurring peers Left: %@, Right: %@", leftConcurringPeers
, rightConcurringPeers
);
258 CFReleaseNull(leftConcurringPeers
);
259 CFReleaseNull(rightConcurringPeers
);
262 CFArrayRef leftApplicants
= SOSAccountCopyApplicants(left
, &error
);
263 ok(leftApplicants
, "Left Applicants (%@) - %s", error
, label
);
265 CFArrayRef rightApplicants
= SOSAccountCopyApplicants(right
, &error
);
266 ok(rightApplicants
, "Left Applicants (%@) - %s", error
, label
);
268 ok(CFEqual(leftApplicants
, rightApplicants
), "Matching applicants (%s) Left: %@, Right: %@", label
, leftApplicants
, rightApplicants
);
270 CFReleaseNull(leftApplicants
);
271 CFReleaseNull(rightApplicants
);
275 static inline void accounts_agree(char *label
, SOSAccountRef left
, SOSAccountRef right
)
277 accounts_agree_internal(label
, left
, right
, true);
285 static inline CFStringRef
CFArrayCopyCompactDescription(CFArrayRef array
) {
287 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Not an array! %@>"), array
);
289 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
291 __block CFStringRef separator
= CFSTR("");
292 CFArrayForEach(array
, ^(const void *value
) {
293 CFStringAppendFormat(result
, NULL
, CFSTR("%@%@"), separator
, value
);
294 separator
= CFSTR(",");
297 CFStringAppend(result
, CFSTR("]"));
299 CFReleaseSafe(separator
);
304 static inline CFStringRef
SOSAccountCopyName(SOSAccountRef account
) {
305 SOSPeerInfoRef pi
= SOSAccountGetMyPeerInfo(account
);
307 return pi
? CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerName(pi
)) : CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("%@"), account
);
310 static inline CFStringRef
CopyChangesDescription(CFDictionaryRef changes
) {
312 CFStringRef pendingChanges
= CFDictionaryCopyCompactDescription((CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
));
314 CFMutableStringRef peerTable
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
316 __block CFStringRef separator
= CFSTR("");
318 CFDictionaryForEach(changes
, ^(const void *key
, const void *value
) {
319 if (CFGetTypeID(key
) == SOSAccountGetTypeID()) {
320 CFStringRef accountName
= SOSAccountCopyName((SOSAccountRef
) key
);
321 CFStringRef arrayDescription
= CFArrayCopyCompactDescription(value
);
323 CFStringAppendFormat(peerTable
, NULL
, CFSTR("%@%@:%@"), separator
, accountName
, arrayDescription
);
324 separator
= CFSTR(", ");
326 CFReleaseSafe(accountName
);
327 CFReleaseSafe(arrayDescription
);
331 CFStringAppend(peerTable
, CFSTR("]"));
333 CFStringRef result
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<TestChanges %@ %@>"), pendingChanges
, peerTable
);
334 CFReleaseNull(pendingChanges
);
335 CFReleaseNull(peerTable
);
340 static void CFDictionaryOverlayDictionary(CFMutableDictionaryRef target
, CFMutableDictionaryRef overlay
) {
341 CFMutableSetRef keysToRemove
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
343 CFDictionaryForEach(overlay
, ^(const void *key
, const void *value
) {
344 const void *current_value
= CFDictionaryGetValue(target
, key
);
345 if (CFEqualSafe(current_value
, value
) || (isNull(value
) && current_value
== NULL
)) {
346 CFSetAddValue(keysToRemove
, key
);
348 CFDictionarySetValue(target
, key
, value
);
352 CFSetForEach(keysToRemove
, ^(const void *value
) {
353 CFDictionaryRemoveValue(overlay
, value
);
356 CFReleaseNull(keysToRemove
);
359 static void CFArrayAppendKeys(CFMutableArrayRef keys
, CFDictionaryRef newKeysToAdd
) {
360 CFDictionaryForEach(newKeysToAdd
, ^(const void *key
, const void *value
) {
361 CFArrayAppendValue(keys
, key
);
365 static bool AddNewChanges(CFMutableDictionaryRef changesRecord
, CFMutableDictionaryRef newKeysAndValues
, SOSAccountRef sender
)
367 __block
bool changes_added
= false;
368 CFMutableDictionaryRef emptyDictionary
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
369 CFDictionaryAddValue(changesRecord
, kCFNull
, emptyDictionary
);
370 CFReleaseNull(emptyDictionary
);
372 CFDictionaryOverlayDictionary((CFMutableDictionaryRef
) CFDictionaryGetValue(changesRecord
, kCFNull
), newKeysAndValues
);
374 CFDictionaryForEach(changesRecord
, ^(const void *key
, const void *value
) {
375 if (isArray(value
) && (sender
== NULL
|| sender
!= key
)) {
376 CFArrayAppendKeys((CFMutableArrayRef
) value
, newKeysAndValues
);
377 if (CFDictionaryGetCount(newKeysAndValues
))
378 changes_added
= true;
383 secnotice("changes", "Changes from %@: %@", sender
, newKeysAndValues
);
385 CFDictionaryRemoveAllValues(newKeysAndValues
);
387 return changes_added
;
390 static bool FillAllChanges(CFMutableDictionaryRef changes
) {
391 __block
bool changed
= false;
393 CFMutableSetRef changedAccounts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
);
395 CFArrayForEach(key_transports
, ^(const void *value
) {
396 SOSTransportKeyParameterTestRef tpt
= (SOSTransportKeyParameterTestRef
) value
;
397 if (AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
))) {
399 CFSetAddValue(changedAccounts
, SOSTransportKeyParameterTestGetAccount(tpt
));
401 SOSTransportKeyParameterTestClearChanges(tpt
);
403 CFArrayForEach(circle_transports
, ^(const void *value
) {
404 SOSTransportCircleTestRef tpt
= (SOSTransportCircleTestRef
) value
;
405 if (AddNewChanges(changes
, SOSTransportCircleTestGetChanges(tpt
), SOSTransportCircleTestGetAccount(tpt
))) {
407 CFSetAddValue(changedAccounts
, SOSTransportCircleTestGetAccount(tpt
));
409 SOSTransportCircleTestClearChanges(tpt
);
411 CFArrayForEach(message_transports
, ^(const void *value
) {
412 if(SOSTransportMessageGetTransportType((SOSTransportMessageRef
)value
, NULL
) == kKVSTest
){
413 SOSTransportMessageTestRef tpt
= (SOSTransportMessageTestRef
) value
;
414 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt
), kCFNull
);
415 if (AddNewChanges(changes
, SOSTransportMessageTestGetChanges(tpt
), SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
))) {
417 CFSetAddValue(changedAccounts
, SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
));
419 SOSTransportMessageTestClearChanges(tpt
);
421 else if(SOSTransportMessageGetTransportType((SOSTransportMessageRef
)value
, NULL
) == kIDSTest
){
422 SOSTransportMessageRef ids
= (SOSTransportMessageRef
) value
;
423 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(ids
), kCFNull
);
424 if (AddNewChanges(changes
, SOSTransportMessageIDSTestGetChanges(ids
), SOSTransportMessageTestGetAccount(ids
))) {
426 CFSetAddValue(changedAccounts
, SOSTransportMessageTestGetAccount(ids
));
428 SOSTransportMessageIDSTestClearChanges(ids
);
432 secnotice("process-changes", "Accounts with change (%@): %@", changed
? CFSTR("YES") : CFSTR("NO"), changedAccounts
);
434 CFReleaseNull(changedAccounts
);
439 static void FillChanges(CFMutableDictionaryRef changes
, SOSAccountRef forAccount
)
441 CFArrayForEach(key_transports
, ^(const void *value
) {
442 SOSTransportKeyParameterTestRef tpt
= (SOSTransportKeyParameterTestRef
) value
;
443 if(CFEqualSafe(forAccount
, SOSTransportKeyParameterTestGetAccount(tpt
))){
444 AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
));
445 SOSTransportKeyParameterTestClearChanges(tpt
);
448 CFArrayForEach(circle_transports
, ^(const void *value
) {
449 SOSTransportCircleTestRef tpt
= (SOSTransportCircleTestRef
) value
;
450 if(CFEqualSafe(forAccount
, SOSTransportCircleTestGetAccount(tpt
))){
451 AddNewChanges(changes
, SOSTransportCircleTestGetChanges(tpt
), SOSTransportCircleTestGetAccount(tpt
));
452 SOSTransportCircleTestClearChanges(tpt
);
455 CFArrayForEach(message_transports
, ^(const void *value
) {
456 if(SOSTransportMessageGetTransportType((SOSTransportMessageRef
)value
, NULL
) == kKVSTest
){
457 SOSTransportMessageTestRef tpt
= (SOSTransportMessageTestRef
) value
;
458 if(CFEqualSafe(forAccount
, SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
))){
459 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt
), kCFNull
);
460 AddNewChanges(changes
, SOSTransportMessageTestGetChanges(tpt
), SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
));
461 SOSTransportMessageTestClearChanges(tpt
);
465 SOSTransportMessageRef tpt
= (SOSTransportMessageRef
) value
;
466 if(CFEqualSafe(forAccount
, SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
))){
467 CFDictionaryRemoveValue(SOSTransportMessageIDSTestGetChanges(tpt
), kCFNull
);
468 AddNewChanges(changes
, SOSTransportMessageIDSTestGetChanges(tpt
), SOSTransportMessageTestGetAccount((SOSTransportMessageRef
)tpt
));
469 SOSTransportMessageIDSTestClearChanges(tpt
);
476 static inline void FillChangesMulti(CFMutableDictionaryRef changes
, SOSAccountRef account
, ...)
478 SOSAccountRef next_account
= account
;
480 va_start(argp
, account
);
481 while(next_account
!= NULL
) {
482 FillChanges(changes
, next_account
);
483 next_account
= va_arg(argp
, SOSAccountRef
);
487 static inline CFMutableArrayRef
CFDictionaryCopyKeys(CFDictionaryRef dictionary
)
489 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
491 CFArrayAppendKeys(result
, dictionary
);
496 #define kFeedChangesToTestCount 1
497 static inline void FeedChangesTo(CFMutableDictionaryRef changes
, SOSAccountRef account
)
499 CFDictionaryRef full_list
= (CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
);
501 if (!isDictionary(full_list
))
502 return; // Nothing recorded to send!
504 CFMutableArrayRef account_pending_keys
= (CFMutableArrayRef
)CFDictionaryGetValue(changes
, account
);
506 if (!isArray(account_pending_keys
)) {
507 account_pending_keys
= CFDictionaryCopyKeys(full_list
);
508 CFDictionaryAddValue(changes
, account
, account_pending_keys
);
509 CFReleaseSafe(account_pending_keys
); // The dictionary keeps it, we don't retain it here.
512 CFMutableDictionaryRef account_pending_messages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
513 CFArrayForEach(account_pending_keys
, ^(const void *value
) {
514 CFDictionaryAddValue(account_pending_messages
, value
, CFDictionaryGetValue(full_list
, value
));
517 secnotice("changes", "Changes for %@:", SOSTransportKeyParameterTestGetName((SOSTransportKeyParameterTestRef
) account
->key_transport
));
519 CFDictionaryForEach(account_pending_messages
, ^(const void *key
, const void *value
) {
520 secnotice("changes", " %@", key
);
523 __block CFMutableArrayRef handled
= NULL
;
524 SOSAccountWithTransactionSync(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
525 __block CFErrorRef error
= NULL
;
526 ok(handled
= SOSTransportDispatchMessages(txn
, account_pending_messages
, &error
), "SOSTransportHandleMessages failed (%@)", error
);
527 CFReleaseNull(error
);
530 if (isArray(handled
)) {
531 CFArrayForEach(handled
, ^(const void *value
) {
532 CFArrayRemoveAllValue(account_pending_keys
, value
);
535 CFReleaseNull(account_pending_messages
);
536 CFReleaseNull(handled
);
539 #define kFeedChangesToMultieTestCountPer 1
541 static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes
, va_list argp
)
543 SOSAccountRef account
= NULL
;
544 while((account
= va_arg(argp
, SOSAccountRef
)) != NULL
) {
545 FeedChangesTo(changes
, account
);
549 static inline void FeedChangesToMulti(CFMutableDictionaryRef changes
, ...)
552 va_start(argp
, changes
);
554 FeedChangesToMultiV(changes
, argp
);
559 static inline void InjectChangeToMulti(CFMutableDictionaryRef changes
,
560 CFStringRef changeKey
, CFTypeRef changeValue
, ...)
562 CFMutableDictionaryRef changes_to_send
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
563 changeKey
, changeValue
,
565 AddNewChanges(changes
, changes_to_send
, NULL
);
566 CFReleaseNull(changes_to_send
);
569 va_start(argp
, changeValue
);
570 FeedChangesToMultiV(changes
, argp
);
574 static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes
, va_list argp
)
576 bool result
= FillAllChanges(changes
);
578 FeedChangesToMultiV(changes
, argp
);
584 static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes
, ...)
587 va_start(argp
, changes
);
589 bool result
= ProcessChangesOnceV(changes
, argp
);
596 static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes
, ...)
599 va_start(argp
, changes
);
602 bool new_data
= false;
605 va_copy(argp_copy
, argp
);
607 new_data
= ProcessChangesOnceV(changes
, argp_copy
);
621 // MARK: Account creation
624 static CFStringRef
modelFromType(SOSPeerInfoDeviceClass cls
) {
626 case SOSPeerInfo_macOS
: return CFSTR("Mac Pro");
627 case SOSPeerInfo_iOS
: return CFSTR("iPhone");
628 case SOSPeerInfo_iCloud
: return CFSTR("iCloud");
629 case SOSPeerInfo_watchOS
: return CFSTR("needWatchOSDeviceName");
630 case SOSPeerInfo_tvOS
: return CFSTR("needTVOSDeviceName");
631 default: return CFSTR("GENERICOSTHING");
635 static inline SOSAccountRef
CreateAccountForLocalChangesWithStartingAttributes(CFStringRef name
, CFStringRef data_source_name
, SOSPeerInfoDeviceClass devclass
, CFStringRef serial
, CFBooleanRef preferIDS
, CFBooleanRef preferIDSFragmentation
, CFBooleanRef preferIDSACKModel
, CFStringRef transportType
, CFStringRef deviceID
) {
637 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
638 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
639 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
640 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
643 CFMutableDictionaryRef gestalt
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
644 CFDictionaryAddValue(gestalt
, kPIUserDefinedDeviceNameKey
, name
);
645 CFDictionaryAddValue(gestalt
, kPIDeviceModelNameKey
, modelFromType(devclass
));
646 CFDictionaryAddValue(gestalt
, kPIOSVersionKey
, CFSTR("TESTRUN"));
648 CFMutableDictionaryRef testV2dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
649 CFDictionaryAddValue(testV2dict
, sSerialNumberKey
, serial
);
650 CFDictionaryAddValue(testV2dict
, sPreferIDS
, preferIDS
);
651 CFDictionaryAddValue(testV2dict
, sPreferIDSFragmentation
, preferIDSFragmentation
);
652 CFDictionaryAddValue(testV2dict
, sPreferIDSACKModel
, preferIDSACKModel
);
653 CFDictionaryAddValue(testV2dict
, sTransportType
, transportType
);
654 CFDictionaryAddValue(testV2dict
, sDeviceID
, deviceID
);
655 SOSAccountRef result
= SOSAccountCreateTest(kCFAllocatorDefault
, name
, gestalt
, factory
);
656 SOSAccountUpdateV2Dictionary(result
, testV2dict
);
658 CFReleaseSafe(SOSAccountCopyUUID(result
));
660 CFReleaseNull(gestalt
);
661 CFReleaseNull(testV2dict
);
666 static CFStringRef sGestaltTest
= CFSTR("GestaltTest");
667 static CFStringRef sV2Test
= CFSTR("V2Test");
668 static inline CFDictionaryRef
SOSTestSaveStaticAccountState(SOSAccountRef account
) {
669 CFMutableDictionaryRef retval
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
670 CFDictionaryRef gestalt
= SOSAccountCopyGestalt(account
);
671 CFDictionaryRef v2dictionary
= SOSAccountCopyV2Dictionary(account
);
672 CFDictionaryAddValue(retval
, sGestaltTest
, gestalt
);
673 CFDictionaryAddValue(retval
, sV2Test
, v2dictionary
);
674 CFReleaseNull(gestalt
);
675 CFReleaseNull(v2dictionary
);
679 static inline void SOSTestRestoreAccountState(SOSAccountRef account
, CFDictionaryRef saved
) {
680 SOSAccountUpdateGestalt(account
, CFDictionaryGetValue(saved
, sGestaltTest
));
681 SOSAccountUpdateV2Dictionary(account
, CFDictionaryGetValue(saved
, sV2Test
));
684 static CFStringRef
CFStringCreateRandomHexWithLength(size_t len
) {
686 CFDataRef data
= CFDataCreateWithRandomBytes(len
/2);
687 CFMutableStringRef retval
= CFStringCreateMutable(kCFAllocatorDefault
, len
);
688 CFStringAppendHexData(retval
, data
);
693 static inline SOSAccountRef
CreateAccountForLocalChanges(CFStringRef name
, CFStringRef data_source_name
)
695 CFStringRef randomSerial
= CFStringCreateRandomHexWithLength(8);
696 CFStringRef randomDevID
= CFStringCreateRandomHexWithLength(16);
697 SOSAccountRef retval
= CreateAccountForLocalChangesWithStartingAttributes(name
, data_source_name
, SOSPeerInfo_iOS
, randomSerial
,
698 kCFBooleanTrue
, kCFBooleanTrue
, kCFBooleanTrue
, SOSTransportMessageTypeIDSV2
, randomDevID
);
699 CFReleaseNull(randomSerial
);
700 CFReleaseNull(randomDevID
);
704 static inline SOSAccountRef
CreateAccountForLocalChangesFromData(CFDataRef flattenedData
, CFStringRef name
, CFStringRef data_source_name
)
706 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
707 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
708 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
709 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
712 SOSAccountRef result
= SOSAccountCreateTestFromData(kCFAllocatorDefault
, flattenedData
, name
, factory
);
719 static inline int countPeers(SOSAccountRef account
) {
720 CFErrorRef error
= NULL
;
723 peers
= SOSAccountCopyPeers(account
, &error
);
724 int retval
= (int) CFArrayGetCount(peers
);
725 CFReleaseNull(error
);
726 CFReleaseNull(peers
);
730 static inline int countActivePeers(SOSAccountRef account
) {
731 CFErrorRef error
= NULL
;
734 peers
= SOSAccountCopyActivePeers(account
, &error
);
735 int retval
= (int) CFArrayGetCount(peers
);
736 CFReleaseNull(error
);
737 CFReleaseNull(peers
);
741 static inline int countActiveValidPeers(SOSAccountRef account
) {
742 CFErrorRef error
= NULL
;
745 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
746 int retval
= (int) CFArrayGetCount(peers
);
747 CFReleaseNull(error
);
748 CFReleaseNull(peers
);
752 static inline int countApplicants(SOSAccountRef account
) {
753 CFErrorRef error
= NULL
;
754 CFArrayRef applicants
= SOSAccountCopyApplicants(account
, &error
);
757 if(applicants
) retval
= (int)CFArrayGetCount(applicants
);
758 CFReleaseNull(error
);
759 CFReleaseNull(applicants
);
764 static inline void showActiveValidPeers(SOSAccountRef account
) {
765 CFErrorRef error
= NULL
;
768 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
769 CFArrayForEach(peers
, ^(const void *value
) {
770 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
771 ok(0, "Active Valid Peer %@", pi
);
773 CFReleaseNull(peers
);
776 #define ok_or_quit(COND,MESSAGE,LABEL) ok(COND, MESSAGE); if(!(COND)) goto LABEL
778 static inline bool testAccountPersistence(SOSAccountRef account
) {
779 SOSDataSourceFactoryRef test_factory
= SOSTestDataSourceFactoryCreate();
780 SOSDataSourceRef test_source
= SOSTestDataSourceCreate();
781 SOSTestDataSourceFactorySetDataSource(test_factory
, CFSTR("TestType"), test_source
);
782 CFErrorRef error
= NULL
;
784 SOSAccountRef reinflatedAccount
= NULL
;
785 CFDataRef accountDER
= NULL
;
787 SOSAccountCheckHasBeenInSync_wTxn(account
);
789 // DER encode account to accountData - this allows checking discreet DER functions
790 size_t size
= SOSAccountGetDEREncodedSize(account
, &error
);
791 CFReleaseNull(error
);
792 uint8_t buffer
[size
];
793 uint8_t* start
= SOSAccountEncodeToDER(account
, &error
, buffer
, buffer
+ sizeof(buffer
));
794 CFReleaseNull(error
);
796 ok_or_quit(start
, "successful encoding", errOut
);
797 ok_or_quit(start
== buffer
, "Used whole buffer", errOut
);
799 accountDER
= CFDataCreate(kCFAllocatorDefault
, buffer
, size
);
800 ok_or_quit(accountDER
, "Made CFData for Account", errOut
);
803 // Re-inflate to "inflated"
804 reinflatedAccount
= SOSAccountCreateFromData(kCFAllocatorDefault
, accountDER
, test_factory
, &error
);
805 CFReleaseNull(error
);
806 CFReleaseNull(accountDER
);
808 ok(reinflatedAccount
, "inflated");
809 ok(CFEqualSafe(reinflatedAccount
, account
), "Compares");
811 // Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
812 accountDER
= SOSAccountCopyEncodedData(reinflatedAccount
, kCFAllocatorDefault
, &error
);
813 CFReleaseNull(error
);
814 CFReleaseNull(reinflatedAccount
);
815 reinflatedAccount
= SOSAccountCreateFromData(kCFAllocatorDefault
, accountDER
, test_factory
, &error
);
816 ok(reinflatedAccount
, "inflated2");
817 ok(CFEqual(account
, reinflatedAccount
), "Compares");
821 CFReleaseNull(reinflatedAccount
);
822 CFReleaseNull(accountDER
);
826 static inline bool SOSTestStartCircleWithAccount(SOSAccountRef account
, CFMutableDictionaryRef changes
, CFStringRef cfaccount
, CFDataRef cfpassword
) {
828 require(SOSAccountAssertUserCredentialsAndUpdate(account
, cfaccount
, cfpassword
, NULL
), retOut
);
829 is(ProcessChangesUntilNoChange(changes
, account
, NULL
), 1, "updates");
830 require(SOSAccountResetToOffering_wTxn(account
, NULL
), retOut
);
831 is(ProcessChangesUntilNoChange(changes
, account
, NULL
), 1, "updates");
837 static inline bool SOSTestApproveRequest(SOSAccountRef approver
, CFIndex napplicants
) {
839 CFErrorRef error
= NULL
;
840 CFArrayRef applicants
= SOSAccountCopyApplicants(approver
, &error
);
842 ok(applicants
&& CFArrayGetCount(applicants
) == napplicants
, "See %ld applicant(s) %@ (%@)", napplicants
, applicants
, error
);
843 CFStringRef approvername
= SOSAccountCopyName(approver
);
844 ok((retval
= SOSAccountAcceptApplicants(approver
, applicants
, &error
)), "%@ accepts (%@)", approvername
, error
);
845 CFReleaseNull(error
);
846 CFReleaseNull(applicants
);
847 CFReleaseNull(approvername
);
852 #define DROP_USERKEY true
853 #define KEEP_USERKEY false
855 static inline bool SOSTestJoinWith(CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
, SOSAccountRef joiner
) {
856 CFErrorRef error
= NULL
;
857 // retval will return op failures, not count failures - we'll still report those from in here.
860 FeedChangesTo(changes
, joiner
);
862 ok(SOSAccountAssertUserCredentialsAndUpdate(joiner
, cfaccount
, cfpassword
, &error
), "Credential setting (%@)", error
);
863 CFReleaseNull(error
);
865 ProcessChangesUntilNoChange(changes
, joiner
, NULL
);
867 ok(retval
= SOSAccountJoinCircles_wTxn(joiner
, &error
), "Applying (%@)", error
);
868 CFReleaseNull(error
);
872 static inline bool SOSTestJoinWithApproval(CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
, SOSAccountRef approver
, SOSAccountRef joiner
, bool dropUserKey
, int expectedCount
, bool expectCleanup
) {
873 //CFErrorRef error = NULL;
874 // retval will return op failures, not count failures - we'll still report those from in here.
877 ok(retval
= SOSTestJoinWith(cfpassword
, cfaccount
, changes
, joiner
), "Applyication Made");
879 is(ProcessChangesUntilNoChange(changes
, approver
, joiner
, NULL
), 2, "updates");
882 if(dropUserKey
) SOSAccountPurgePrivateCredential(joiner
); // lose the userKey so we don't "fix" the ghost problem yet.
885 if(expectCleanup
) nrounds
++;
887 ok(retval
&= SOSTestApproveRequest(approver
, 1), "Accepting Request to Join");
888 is(ProcessChangesUntilNoChange(changes
, approver
, joiner
, NULL
), nrounds
, "updates");
890 accounts_agree_internal("Successful join shows same circle view", joiner
, approver
, false);
891 is(countPeers(joiner
), expectedCount
, "There should be %d valid peers", expectedCount
);
896 static inline SOSAccountRef
SOSTestCreateAccountAsSerialClone(CFStringRef name
, SOSPeerInfoDeviceClass devClass
, CFStringRef serial
, CFStringRef idsID
) {
897 return CreateAccountForLocalChangesWithStartingAttributes(name
, CFSTR("TestSource"), devClass
, serial
, kCFBooleanTrue
, kCFBooleanTrue
, kCFBooleanTrue
, SOSTransportMessageTypeIDSV2
, idsID
);
900 static inline bool SOSTestMakeGhostInCircle(CFStringRef name
, SOSPeerInfoDeviceClass devClass
, CFStringRef serial
, CFStringRef idsID
,
901 CFDataRef cfpassword
, CFStringRef cfaccount
, CFMutableDictionaryRef changes
,
902 SOSAccountRef approver
, int expectedCount
) {
904 SOSAccountRef ghostAccount
= SOSTestCreateAccountAsSerialClone(name
, devClass
, serial
, idsID
);
905 ok(ghostAccount
, "Created Ghost Account");
906 require_quiet(ghostAccount
, retOut
);
907 if(!ghostAccount
) return false;
908 ok(retval
= SOSTestJoinWithApproval(cfpassword
, cfaccount
, changes
, approver
, ghostAccount
, DROP_USERKEY
, expectedCount
, true), "Ghost Joined Circle with expected result");
909 CFReleaseNull(ghostAccount
);
914 static inline bool SOSTestChangeAccountDeviceName(SOSAccountRef account
, CFStringRef name
) {
916 CFMutableDictionaryRef mygestalt
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerGetGestalt(SOSAccountGetMyPeerInfo(account
)));
917 require_quiet(mygestalt
, retOut
);
918 CFDictionarySetValue(mygestalt
, kPIUserDefinedDeviceNameKey
, name
);
919 retval
= SOSAccountUpdateGestalt(account
, mygestalt
);
921 CFReleaseNull(mygestalt
);
925 static inline void SOSTestCleanup() {
926 SOSUnregisterAllTransportMessages();
927 SOSUnregisterAllTransportCircles();
928 SOSUnregisterAllTransportKeyParameters();
929 CFArrayRemoveAllValues(key_transports
);
930 CFArrayRemoveAllValues(circle_transports
);
931 CFArrayRemoveAllValues(message_transports
);