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 "SOSTransportTestTransports.h"
38 #define kAccountsAgreeTestMin 9
39 #define kAccountsAgreeTestPerPeer 1
40 #define accountsAgree(x) (kAccountsAgreeTestMin + kAccountsAgreeTestPerPeer * (x))
42 static void SOSAccountResetToTest(SOSAccountRef a
, CFStringRef accountName
) {
43 SOSUnregisterTransportKeyParameter(a
->key_transport
);
45 CFReleaseNull(a
->circle_transport
);
46 CFReleaseNull(a
->kvs_message_transport
);
47 CFReleaseNull(a
->key_transport
);
49 SOSAccountEnsureFactoryCirclesTest(a
, accountName
);
53 static SOSAccountRef
SOSAccountCreateBasicTest(CFAllocatorRef allocator
,
54 CFStringRef accountName
,
55 CFDictionaryRef gestalt
,
56 SOSDataSourceFactoryRef factory
) {
57 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
62 static SOSAccountRef
SOSAccountCreateTest(CFAllocatorRef allocator
,
63 CFStringRef accountName
,
64 CFDictionaryRef gestalt
,
65 SOSDataSourceFactoryRef factory
) {
66 SOSAccountRef a
= SOSAccountCreateBasicTest(allocator
, accountName
, gestalt
, factory
);
68 SOSAccountResetToTest(a
, accountName
);
73 static SOSAccountRef
SOSAccountCreateTestFromData(CFAllocatorRef allocator
,
75 CFStringRef accountName
,
76 SOSDataSourceFactoryRef factory
) {
77 SOSAccountRef a
= SOSAccountCreateFromData(allocator
, data
, factory
, NULL
);
79 CFDictionaryRef gestalt
= SOSCreatePeerGestaltFromName(accountName
);
80 a
= SOSAccountCreate(allocator
, gestalt
, factory
);
81 CFReleaseNull(gestalt
);
84 SOSAccountResetToTest(a
, accountName
);
90 static inline bool SOSAccountAssertUserCredentialsAndUpdate(SOSAccountRef account
,
91 CFStringRef user_account
, CFDataRef user_password
,
95 success
= SOSAccountAssertUserCredentials(account
, user_account
, user_password
, error
);
96 require_quiet(success
, done
);
98 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
106 static void unretired_peers_is_subset(const char* label
, CFArrayRef peers
, CFSetRef allowed_peers
)
108 CFArrayForEach(peers
, ^(const void *value
) {
109 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
111 CFErrorRef leftError
= NULL
;
112 CFErrorRef rightError
= NULL
;
114 ok(SOSPeerInfoIsRetirementTicket(pi
) || SOSPeerInfoIsCloudIdentity(pi
) || CFSetContainsValue(allowed_peers
, pi
), "Peer is allowed (%s) Peer: %@, Allowed %@", label
, pi
, allowed_peers
);
116 CFReleaseNull(leftError
);
117 CFReleaseNull(rightError
);
121 static void accounts_agree_internal(char *label
, SOSAccountRef left
, SOSAccountRef right
, bool check_peers
)
123 CFErrorRef error
= NULL
;
125 CFArrayRef leftPeers
= SOSAccountCopyActivePeers(left
, &error
);
126 ok(leftPeers
, "Left peers (%@) - %s", error
, label
);
127 CFReleaseNull(error
);
129 CFArrayRef rightPeers
= SOSAccountCopyActivePeers(right
, &error
);
130 ok(rightPeers
, "Right peers (%@) - %s", error
, label
);
131 CFReleaseNull(error
);
133 ok(CFEqual(leftPeers
, rightPeers
), "Matching peers (%s) Left: %@, Right: %@", label
, leftPeers
, rightPeers
);
136 CFMutableSetRef allowed_identities
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
138 SOSFullPeerInfoRef leftFullPeer
= SOSAccountCopyAccountIdentityPeerInfo(left
, kCFAllocatorDefault
, NULL
);
141 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(leftFullPeer
));
143 CFReleaseNull(leftFullPeer
);
145 SOSFullPeerInfoRef rightFullPeer
= SOSAccountCopyAccountIdentityPeerInfo(right
, kCFAllocatorDefault
, NULL
);
148 CFSetAddValue(allowed_identities
, SOSFullPeerInfoGetPeerInfo(rightFullPeer
));
150 CFReleaseNull(rightFullPeer
);
152 unretired_peers_is_subset(label
, leftPeers
, allowed_identities
);
154 CFReleaseNull(allowed_identities
);
157 CFReleaseNull(leftPeers
);
158 CFReleaseNull(rightPeers
);
161 CFArrayRef leftConcurringPeers
= SOSAccountCopyConcurringPeers(left
, &error
);
162 ok(leftConcurringPeers
, "Left peers (%@) - %s", error
, label
);
164 CFArrayRef rightConcurringPeers
= SOSAccountCopyConcurringPeers(right
, &error
);
165 ok(rightConcurringPeers
, "Right peers (%@) - %s", error
, label
);
167 ok(CFEqual(leftConcurringPeers
, rightConcurringPeers
), "Matching concurring peers Left: %@, Right: %@", leftConcurringPeers
, rightConcurringPeers
);
169 CFReleaseNull(leftConcurringPeers
);
170 CFReleaseNull(rightConcurringPeers
);
173 CFArrayRef leftApplicants
= SOSAccountCopyApplicants(left
, &error
);
174 ok(leftApplicants
, "Left Applicants (%@) - %s", error
, label
);
176 CFArrayRef rightApplicants
= SOSAccountCopyApplicants(right
, &error
);
177 ok(rightApplicants
, "Left Applicants (%@) - %s", error
, label
);
179 ok(CFEqual(leftApplicants
, rightApplicants
), "Matching applicants (%s) Left: %@, Right: %@", label
, leftApplicants
, rightApplicants
);
181 CFReleaseNull(leftApplicants
);
182 CFReleaseNull(rightApplicants
);
186 static inline void accounts_agree(char *label
, SOSAccountRef left
, SOSAccountRef right
)
188 accounts_agree_internal(label
, left
, right
, true);
196 static inline CFStringRef
CFArrayCopyCompactDescription(CFArrayRef array
) {
198 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Not an array! %@>"), array
);
200 CFMutableStringRef result
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
202 __block CFStringRef separator
= CFSTR("");
203 CFArrayForEach(array
, ^(const void *value
) {
204 CFStringAppendFormat(result
, NULL
, CFSTR("%@%@"), separator
, value
);
205 separator
= CFSTR(",");
208 CFStringAppend(result
, CFSTR("]"));
210 CFReleaseSafe(separator
);
215 static inline CFStringRef
SOSAccountCopyName(SOSAccountRef account
) {
216 SOSPeerInfoRef pi
= SOSAccountGetMyPeerInfo(account
);
218 return pi
? CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerName(pi
)) : CFStringCreateWithFormat(kCFAllocatorDefault
, 0, CFSTR("%@"), account
);
221 static inline CFStringRef
CopyChangesDescription(CFDictionaryRef changes
) {
223 CFStringRef pendingChanges
= CFDictionaryCopyCompactDescription((CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
));
225 CFMutableStringRef peerTable
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, CFSTR("["));
227 __block CFStringRef separator
= CFSTR("");
229 CFDictionaryForEach(changes
, ^(const void *key
, const void *value
) {
230 if (CFGetTypeID(key
) == SOSAccountGetTypeID()) {
231 CFStringRef accountName
= SOSAccountCopyName((SOSAccountRef
) key
);
232 CFStringRef arrayDescription
= CFArrayCopyCompactDescription(value
);
234 CFStringAppendFormat(peerTable
, NULL
, CFSTR("%@%@:%@"), separator
, accountName
, arrayDescription
);
235 separator
= CFSTR(", ");
237 CFReleaseSafe(accountName
);
238 CFReleaseSafe(arrayDescription
);
242 CFStringAppend(peerTable
, CFSTR("]"));
244 CFStringRef result
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<TestChanges %@ %@>"), pendingChanges
, peerTable
);
245 CFReleaseNull(pendingChanges
);
246 CFReleaseNull(peerTable
);
251 static void CFDictionaryOverlayDictionary(CFMutableDictionaryRef target
, CFMutableDictionaryRef overlay
) {
252 CFMutableSetRef keysToRemove
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
254 CFDictionaryForEach(overlay
, ^(const void *key
, const void *value
) {
255 const void *current_value
= CFDictionaryGetValue(target
, key
);
256 if (CFEqualSafe(current_value
, value
) || (isNull(value
) && current_value
== NULL
)) {
257 CFSetAddValue(keysToRemove
, key
);
259 CFDictionarySetValue(target
, key
, value
);
263 CFSetForEach(keysToRemove
, ^(const void *value
) {
264 CFDictionaryRemoveValue(overlay
, value
);
267 CFReleaseNull(keysToRemove
);
270 static void CFArrayAppendKeys(CFMutableArrayRef keys
, CFDictionaryRef newKeysToAdd
) {
271 CFDictionaryForEach(newKeysToAdd
, ^(const void *key
, const void *value
) {
272 CFArrayAppendValue(keys
, key
);
276 static bool AddNewChanges(CFMutableDictionaryRef changesRecord
, CFMutableDictionaryRef newKeysAndValues
, SOSAccountRef sender
)
278 __block
bool changes_added
= false;
279 CFMutableDictionaryRef emptyDictionary
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
280 CFDictionaryAddValue(changesRecord
, kCFNull
, emptyDictionary
);
281 CFReleaseNull(emptyDictionary
);
283 CFDictionaryOverlayDictionary((CFMutableDictionaryRef
) CFDictionaryGetValue(changesRecord
, kCFNull
), newKeysAndValues
);
285 CFDictionaryForEach(changesRecord
, ^(const void *key
, const void *value
) {
286 if (isArray(value
) && (sender
== NULL
|| sender
!= key
)) {
287 CFArrayAppendKeys((CFMutableArrayRef
) value
, newKeysAndValues
);
288 if (CFDictionaryGetCount(newKeysAndValues
))
289 changes_added
= true;
294 secnotice("changes", "Changes from %@: %@", sender
, newKeysAndValues
);
296 CFDictionaryRemoveAllValues(newKeysAndValues
);
298 return changes_added
;
301 static bool FillAllChanges(CFMutableDictionaryRef changes
) {
302 __block
bool changed
= false;
304 CFMutableSetRef changedAccounts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, NULL
);
306 CFArrayForEach(key_transports
, ^(const void *value
) {
307 SOSTransportKeyParameterTestRef tpt
= (SOSTransportKeyParameterTestRef
) value
;
308 if (AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
))) {
310 CFSetAddValue(changedAccounts
, SOSTransportKeyParameterTestGetAccount(tpt
));
312 SOSTransportKeyParameterTestClearChanges(tpt
);
314 CFArrayForEach(circle_transports
, ^(const void *value
) {
315 SOSTransportCircleTestRef tpt
= (SOSTransportCircleTestRef
) value
;
316 if (AddNewChanges(changes
, SOSTransportCircleTestGetChanges(tpt
), SOSTransportCircleTestGetAccount(tpt
))) {
318 CFSetAddValue(changedAccounts
, SOSTransportCircleTestGetAccount(tpt
));
320 SOSTransportCircleTestClearChanges(tpt
);
322 CFArrayForEach(message_transports
, ^(const void *value
) {
323 SOSTransportMessageTestRef tpt
= (SOSTransportMessageTestRef
) value
;
324 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt
), kCFNull
);
325 if (AddNewChanges(changes
, SOSTransportMessageTestGetChanges(tpt
), SOSTransportMessageTestGetAccount(tpt
))) {
327 CFSetAddValue(changedAccounts
, SOSTransportMessageTestGetAccount(tpt
));
329 SOSTransportMessageTestClearChanges(tpt
);
332 secnotice("process-changes", "Accounts with change (%@): %@", changed
? CFSTR("YES") : CFSTR("NO"), changedAccounts
);
334 CFReleaseNull(changedAccounts
);
339 static void FillChanges(CFMutableDictionaryRef changes
, SOSAccountRef forAccount
)
341 CFArrayForEach(key_transports
, ^(const void *value
) {
342 SOSTransportKeyParameterTestRef tpt
= (SOSTransportKeyParameterTestRef
) value
;
343 if(CFEqualSafe(forAccount
, SOSTransportKeyParameterTestGetAccount(tpt
))){
344 AddNewChanges(changes
, SOSTransportKeyParameterTestGetChanges(tpt
), SOSTransportKeyParameterTestGetAccount(tpt
));
345 SOSTransportKeyParameterTestClearChanges(tpt
);
348 CFArrayForEach(circle_transports
, ^(const void *value
) {
349 SOSTransportCircleTestRef tpt
= (SOSTransportCircleTestRef
) value
;
350 if(CFEqualSafe(forAccount
, SOSTransportCircleTestGetAccount(tpt
))){
351 AddNewChanges(changes
, SOSTransportCircleTestGetChanges(tpt
), SOSTransportCircleTestGetAccount(tpt
));
352 SOSTransportCircleTestClearChanges(tpt
);
355 CFArrayForEach(message_transports
, ^(const void *value
) {
356 SOSTransportMessageTestRef tpt
= (SOSTransportMessageTestRef
) value
;
357 if(CFEqualSafe(forAccount
, SOSTransportMessageTestGetAccount(tpt
))){
358 CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt
), kCFNull
);
359 AddNewChanges(changes
, SOSTransportMessageTestGetChanges(tpt
), SOSTransportMessageTestGetAccount(tpt
));
360 SOSTransportMessageTestClearChanges(tpt
);
366 static inline void FillChangesMulti(CFMutableDictionaryRef changes
, SOSAccountRef account
, ...)
368 SOSAccountRef next_account
= account
;
370 va_start(argp
, account
);
371 while(next_account
!= NULL
) {
372 FillChanges(changes
, next_account
);
373 next_account
= va_arg(argp
, SOSAccountRef
);
377 static inline CFMutableArrayRef
CFDictionaryCopyKeys(CFDictionaryRef dictionary
)
379 CFMutableArrayRef result
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
381 CFArrayAppendKeys(result
, dictionary
);
386 #define kFeedChangesToTestCount 1
387 static inline void FeedChangesTo(CFMutableDictionaryRef changes
, SOSAccountRef account
)
389 CFDictionaryRef full_list
= (CFDictionaryRef
) CFDictionaryGetValue(changes
, kCFNull
);
391 if (!isDictionary(full_list
))
392 return; // Nothing recorded to send!
394 CFMutableArrayRef account_pending_keys
= (CFMutableArrayRef
)CFDictionaryGetValue(changes
, account
);
396 if (!isArray(account_pending_keys
)) {
397 CFReleaseNull(account_pending_keys
);
399 account_pending_keys
= CFDictionaryCopyKeys(full_list
);
400 CFDictionaryAddValue(changes
, account
, account_pending_keys
);
401 CFReleaseSafe(account_pending_keys
); // The dictionary keeps it, we don't retain it here.
404 CFMutableArrayRef handled
= NULL
;
406 secnotice("changes", "Changes for %@: %@", SOSTransportKeyParameterTestGetName((SOSTransportKeyParameterTestRef
) account
->key_transport
), account_pending_keys
);
408 CFErrorRef error
= NULL
;
409 CFMutableDictionaryRef account_pending_messages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
410 CFArrayForEach(account_pending_keys
, ^(const void *value
) {
411 CFDictionaryAddValue(account_pending_messages
, value
, CFDictionaryGetValue(full_list
, value
));
414 ok(handled
= SOSTransportDispatchMessages(account
, account_pending_messages
, &error
), "SOSTransportHandleMessages failed (%@)", error
);
416 if (isArray(handled
)) {
417 CFArrayForEach(handled
, ^(const void *value
) {
418 CFArrayRemoveAllValue(account_pending_keys
, value
);
421 CFReleaseNull(account_pending_messages
);
422 CFReleaseNull(handled
);
423 CFReleaseNull(error
);
426 #define kFeedChangesToMultieTestCountPer 1
428 static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes
, va_list argp
)
430 SOSAccountRef account
= NULL
;
431 while((account
= va_arg(argp
, SOSAccountRef
)) != NULL
) {
432 FeedChangesTo(changes
, account
);
436 static inline void FeedChangesToMulti(CFMutableDictionaryRef changes
, ...)
439 va_start(argp
, changes
);
441 FeedChangesToMultiV(changes
, argp
);
446 static inline void InjectChangeToMulti(CFMutableDictionaryRef changes
,
447 CFStringRef changeKey
, CFTypeRef changeValue
, ...)
449 CFMutableDictionaryRef changes_to_send
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
450 changeKey
, changeValue
,
452 AddNewChanges(changes
, changes_to_send
, NULL
);
453 CFReleaseNull(changes_to_send
);
456 va_start(argp
, changeValue
);
457 FeedChangesToMultiV(changes
, argp
);
461 static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes
, va_list argp
)
463 bool result
= FillAllChanges(changes
);
465 FeedChangesToMultiV(changes
, argp
);
471 static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes
, ...)
474 va_start(argp
, changes
);
476 bool result
= ProcessChangesOnceV(changes
, argp
);
483 static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes
, ...)
486 va_start(argp
, changes
);
489 bool new_data
= false;
492 va_copy(argp_copy
, argp
);
494 new_data
= ProcessChangesOnceV(changes
, argp_copy
);
508 // MARK: Account creation
512 static inline SOSAccountRef
CreateAccountForLocalChanges(CFStringRef name
, CFStringRef data_source_name
)
514 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
515 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
516 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
517 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
519 CFDictionaryRef gestalt
= SOSCreatePeerGestaltFromName(name
);
521 SOSAccountRef result
= SOSAccountCreateTest(kCFAllocatorDefault
, name
, gestalt
, factory
);
523 CFReleaseNull(gestalt
);
528 static inline SOSAccountRef
CreateAccountForLocalChangesFromData(CFDataRef flattenedData
, CFStringRef name
, CFStringRef data_source_name
)
530 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
531 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
532 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
533 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
536 SOSAccountRef result
= SOSAccountCreateTestFromData(kCFAllocatorDefault
, flattenedData
, name
, factory
);
543 static inline int countPeers(SOSAccountRef account
) {
544 CFErrorRef error
= NULL
;
547 peers
= SOSAccountCopyPeers(account
, &error
);
548 int retval
= (int) CFArrayGetCount(peers
);
549 CFReleaseNull(error
);
550 CFReleaseNull(peers
);
554 static inline int countActivePeers(SOSAccountRef account
) {
555 CFErrorRef error
= NULL
;
558 peers
= SOSAccountCopyActivePeers(account
, &error
);
559 int retval
= (int) CFArrayGetCount(peers
);
560 CFReleaseNull(error
);
561 CFReleaseNull(peers
);
565 static inline int countActiveValidPeers(SOSAccountRef account
) {
566 CFErrorRef error
= NULL
;
569 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
570 int retval
= (int) CFArrayGetCount(peers
);
571 CFReleaseNull(error
);
572 CFReleaseNull(peers
);
576 static inline int countApplicants(SOSAccountRef account
) {
577 CFErrorRef error
= NULL
;
578 CFArrayRef applicants
= SOSAccountCopyApplicants(account
, &error
);
581 if(applicants
) retval
= (int)CFArrayGetCount(applicants
);
582 CFReleaseNull(error
);
583 CFReleaseNull(applicants
);
588 static inline void showActiveValidPeers(SOSAccountRef account
) {
589 CFErrorRef error
= NULL
;
592 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
593 CFArrayForEach(peers
, ^(const void *value
) {
594 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
595 ok(0, "Active Valid Peer %@", pi
);
597 CFReleaseNull(peers
);