2 * Copyright (c) 2012-2016 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@
29 #include <CoreFoundation/CFDictionary.h>
30 #include <utilities/SecCFWrappers.h>
32 #include "keychain/SecureObjectSync/SOSAccount.h"
33 #include "keychain/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
35 #include "secd_regressions.h"
36 #include "SOSAccountTesting.h"
37 #include "SecdTestKeychainUtilities.h"
40 Make a circle with two peers - alice and bob(bob is iOS and serial#"abababababab")
41 have alice leave the circle
42 release bob, make a new bob - iOS and same serial number
43 try to join the circle - it should resetToOffering with ghost fix
45 For phase 1 we expect the ghostfix to work with iOS devices, but not with MacOSX devices.
49 static void hauntedCircle(SOSPeerInfoDeviceClass devClass, bool expectGhostBusted)
51 CFErrorRef error = NULL;
52 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
53 CFStringRef cfaccount = CFSTR("test@test.org");
54 CFStringRef ghostSerialID = CFSTR("abababababab");
55 CFStringRef ghostIdsID = CFSTR("targetIDS");
57 CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
58 SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("TestSource"));
59 SOSAccount* bob_account = SOSTestCreateAccountAsSerialClone(CFSTR("Bob"), devClass, ghostSerialID, ghostIdsID);
62 ok(SOSTestStartCircleWithAccount(alice_account, changes, cfaccount, cfpassword), "Have Alice start a circle");
63 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
64 ok(SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bob_account, KEEP_USERKEY, 2, false), "Bob Joins");
67 ok( [alice_account.trust leaveCircle:alice_account err:&error], "Alice Leaves (%@)", error);
69 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
70 accounts_agree("Alice bails", bob_account, alice_account);
71 is(countPeers(bob_account), 1, "There should only be 1 valid peer");
72 // We're dropping all peers that are in the circle - leaving a circle with only one peer - and that's a ghost
74 // Make new bob - same as the old bob except peerID
76 SOSAccount* bobFinal = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
77 is(ProcessChangesUntilNoChange(changes, bobFinal, NULL), 1, "updates");
79 ok(SOSTestJoinWith(cfpassword, cfaccount, changes, bobFinal), "Application Made");
80 CFReleaseNull(cfpassword);
82 // Did ghostbuster work?
83 is(ProcessChangesUntilNoChange(changes, bobFinal, NULL), 2, "updates");
84 if(expectGhostBusted) { // ghostbusting is currently disabled for MacOSX Peers
85 ok([bobFinal isInCircle:NULL], "Bob is in");
87 ok(![bobFinal isInCircle:NULL], "Bob is not in");
90 is(countPeers(bobFinal), 1, "There should only be 1 valid peer");
92 CFReleaseNull(changes);
100 static void multiBob(SOSPeerInfoDeviceClass devClass, bool expectGhostBusted, bool delayedPrivKey, bool pairJoin) {
101 CFErrorRef error = NULL;
102 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
103 CFStringRef cfaccount = CFSTR("test@test.org");
104 CFStringRef ghostSerialID = CFSTR("abababababab");
105 CFStringRef ghostIdsID = CFSTR("targetIDS");
107 CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
108 SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("TestSource"));
111 ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
112 is(ProcessChangesUntilNoChange(changes, alice_account, NULL), 1, "updates");
114 ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
115 CFReleaseNull(error);
117 is(ProcessChangesUntilNoChange(changes, alice_account, NULL), 1, "updates");
119 SOSTestMakeGhostInCircle(CFSTR("Bob1"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 2);
120 SOSTestMakeGhostInCircle(CFSTR("Bob2"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 3);
121 SOSTestMakeGhostInCircle(CFSTR("Bob3"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 4);
122 SOSTestMakeGhostInCircle(CFSTR("Bob4"), devClass, ghostSerialID, ghostIdsID, cfpassword, cfaccount, changes, alice_account, 5);
124 SOSAccount* bobFinal_account = SOSTestCreateAccountAsSerialClone(CFSTR("BobFinal"), devClass, ghostSerialID, ghostIdsID);
127 SOSTestJoinThroughPiggyBack(cfpassword, cfaccount, changes, alice_account, bobFinal_account, KEEP_USERKEY, 6, true);
128 is(countPeers(bobFinal_account), 6, "Expect ghosts still in circle");
129 } else if(delayedPrivKey) {
130 SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bobFinal_account, DROP_USERKEY, 6, true);
131 is(countPeers(bobFinal_account), 6, "Expect ghosts still in circle");
133 SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bobFinal_account, KEEP_USERKEY, 2, true);
136 if(pairJoin || delayedPrivKey) { // this allows the ghostbusting to be done in a delayed fashion for the instances where that is proper
137 SOSAccountTryUserCredentials(bobFinal_account, cfaccount, cfpassword, &error);
138 ok(SOSTestChangeAccountDeviceName(bobFinal_account, CFSTR("ThereCanBeOnlyOneBob")), "force an unrelated circle change");
139 is(ProcessChangesUntilNoChange(changes, alice_account, bobFinal_account, NULL), 3, "updates");
142 CFReleaseNull(cfpassword);
145 ok([bobFinal_account isInCircle:NULL], "bobFinal_account is in");
147 is(countPeers(bobFinal_account), 2, "Expect ghostBobs to be gone");
148 is(countPeers(alice_account), 2, "Expect ghostBobs to be gone");
149 accounts_agree_internal("Alice and ThereCanBeOnlyOneBob are the only circle peers and they agree", alice_account, bobFinal_account, false);
151 CFReleaseNull(changes);
154 bobFinal_account = nil;
159 static void iosICloudIdentity() {
160 CFErrorRef error = NULL;
161 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
162 CFStringRef cfaccount = CFSTR("test@test.org");
163 CFStringRef ghostIdsID = CFSTR("targetIDS");
165 CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
166 SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("TestSource"));
169 ok(SOSTestStartCircleWithAccount(alice_account, changes, cfaccount, cfpassword), "Have Alice start a circle");
171 SOSCircleRef circle = [alice_account.trust getCircle:NULL];
172 __block CFStringRef serial = NULL;
173 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
174 if(SOSPeerInfoIsCloudIdentity(peer)) {
175 serial = SOSPeerInfoCopySerialNumber(peer);
179 SOSAccount* bob_account = SOSTestCreateAccountAsSerialClone(CFSTR("Bob"), SOSPeerInfo_iOS, serial, ghostIdsID);
181 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
182 ok(SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bob_account, KEEP_USERKEY, 2, false), "Bob Joins");
183 CFReleaseNull(cfpassword);
185 circle = [alice_account.trust getCircle:&error];
186 __block bool hasiCloudIdentity = false;
187 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
188 if(SOSPeerInfoIsCloudIdentity(peer)) {
189 hasiCloudIdentity = true;
193 ok(hasiCloudIdentity, "GhostBusting didn't mess with the iCloud Identity");
195 CFReleaseNull(changes);
201 int secd_68_ghosts(int argc, char *const *argv)
205 secd_test_setup_temp_keychain(__FUNCTION__, NULL);
208 // changing ghostbusting. handleUpdateCircle version is going away.
209 hauntedCircle(SOSPeerInfo_iOS, true);
210 hauntedCircle(SOSPeerInfo_macOS, false);
211 multiBob(SOSPeerInfo_iOS, true, false, false);
212 multiBob(SOSPeerInfo_iOS, false, true, false);
213 multiBob(SOSPeerInfo_iOS, false, false, true); // piggyback join case