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 CFErrorRef error
= NULL
;
407 CFMutableDictionaryRef account_pending_messages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
408 CFArrayForEach(account_pending_keys
, ^(const void *value
) {
409 CFDictionaryAddValue(account_pending_messages
, value
, CFDictionaryGetValue(full_list
, value
));
412 secnotice("changes", "Changes for %@:", SOSTransportKeyParameterTestGetName((SOSTransportKeyParameterTestRef
) account
->key_transport
));
414 CFDictionaryForEach(account_pending_messages
, ^(const void *key
, const void *value
) {
415 secnotice("changes", " %@", key
);
418 ok(handled
= SOSTransportDispatchMessages(account
, account_pending_messages
, &error
), "SOSTransportHandleMessages failed (%@)", error
);
420 if (isArray(handled
)) {
421 CFArrayForEach(handled
, ^(const void *value
) {
422 CFArrayRemoveAllValue(account_pending_keys
, value
);
425 CFReleaseNull(account_pending_messages
);
426 CFReleaseNull(handled
);
427 CFReleaseNull(error
);
430 #define kFeedChangesToMultieTestCountPer 1
432 static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes
, va_list argp
)
434 SOSAccountRef account
= NULL
;
435 while((account
= va_arg(argp
, SOSAccountRef
)) != NULL
) {
436 FeedChangesTo(changes
, account
);
440 static inline void FeedChangesToMulti(CFMutableDictionaryRef changes
, ...)
443 va_start(argp
, changes
);
445 FeedChangesToMultiV(changes
, argp
);
450 static inline void InjectChangeToMulti(CFMutableDictionaryRef changes
,
451 CFStringRef changeKey
, CFTypeRef changeValue
, ...)
453 CFMutableDictionaryRef changes_to_send
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
,
454 changeKey
, changeValue
,
456 AddNewChanges(changes
, changes_to_send
, NULL
);
457 CFReleaseNull(changes_to_send
);
460 va_start(argp
, changeValue
);
461 FeedChangesToMultiV(changes
, argp
);
465 static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes
, va_list argp
)
467 bool result
= FillAllChanges(changes
);
469 FeedChangesToMultiV(changes
, argp
);
475 static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes
, ...)
478 va_start(argp
, changes
);
480 bool result
= ProcessChangesOnceV(changes
, argp
);
487 static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes
, ...)
490 va_start(argp
, changes
);
493 bool new_data
= false;
496 va_copy(argp_copy
, argp
);
498 new_data
= ProcessChangesOnceV(changes
, argp_copy
);
512 // MARK: Account creation
516 static inline SOSAccountRef
CreateAccountForLocalChanges(CFStringRef name
, CFStringRef data_source_name
)
518 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
519 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
520 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
521 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
523 CFDictionaryRef gestalt
= SOSCreatePeerGestaltFromName(name
);
525 SOSAccountRef result
= SOSAccountCreateTest(kCFAllocatorDefault
, name
, gestalt
, factory
);
527 CFReleaseNull(gestalt
);
532 static inline SOSAccountRef
CreateAccountForLocalChangesFromData(CFDataRef flattenedData
, CFStringRef name
, CFStringRef data_source_name
)
534 SOSDataSourceFactoryRef factory
= SOSTestDataSourceFactoryCreate();
535 SOSDataSourceRef ds
= SOSTestDataSourceCreate();
536 SOSTestDataSourceFactorySetDataSource(factory
, data_source_name
, ds
);
537 SOSEngineRef engine
= SOSEngineCreate(ds
, NULL
);
540 SOSAccountRef result
= SOSAccountCreateTestFromData(kCFAllocatorDefault
, flattenedData
, name
, factory
);
547 static inline int countPeers(SOSAccountRef account
) {
548 CFErrorRef error
= NULL
;
551 peers
= SOSAccountCopyPeers(account
, &error
);
552 int retval
= (int) CFArrayGetCount(peers
);
553 CFReleaseNull(error
);
554 CFReleaseNull(peers
);
558 static inline int countActivePeers(SOSAccountRef account
) {
559 CFErrorRef error
= NULL
;
562 peers
= SOSAccountCopyActivePeers(account
, &error
);
563 int retval
= (int) CFArrayGetCount(peers
);
564 CFReleaseNull(error
);
565 CFReleaseNull(peers
);
569 static inline int countActiveValidPeers(SOSAccountRef account
) {
570 CFErrorRef error
= NULL
;
573 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
574 int retval
= (int) CFArrayGetCount(peers
);
575 CFReleaseNull(error
);
576 CFReleaseNull(peers
);
580 static inline int countApplicants(SOSAccountRef account
) {
581 CFErrorRef error
= NULL
;
582 CFArrayRef applicants
= SOSAccountCopyApplicants(account
, &error
);
585 if(applicants
) retval
= (int)CFArrayGetCount(applicants
);
586 CFReleaseNull(error
);
587 CFReleaseNull(applicants
);
592 static inline void showActiveValidPeers(SOSAccountRef account
) {
593 CFErrorRef error
= NULL
;
596 peers
= SOSAccountCopyActiveValidPeers(account
, &error
);
597 CFArrayForEach(peers
, ^(const void *value
) {
598 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
599 ok(0, "Active Valid Peer %@", pi
);
601 CFReleaseNull(peers
);