5 // Created by Richard Murphy on 4/12/16.
9 #include "SOSAccountPriv.h"
10 #include "SOSAccountGhost.h"
11 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
12 #include <Security/SecureObjectSync/SOSInternal.h>
13 #include <Security/SecureObjectSync/SOSAccount.h>
14 #include <Security/SecureObjectSync/SOSCircle.h>
15 #include <Security/SecureObjectSync/SOSPeerInfo.h>
16 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
17 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
19 #define DETECT_IOS_ONLY 1
21 static bool sosGhostCheckValid(SOSPeerInfoRef pi) {
24 require_quiet(pi, retOut);
25 SOSPeerInfoDeviceClass peerClass = SOSPeerInfoGetClass(pi);
28 case SOSPeerInfo_tvOS:
29 case SOSPeerInfo_watchOS:
32 case SOSPeerInfo_macOS:
33 case SOSPeerInfo_iCloud:
34 case SOSPeerInfo_unknown:
46 static CFSetRef SOSCircleCreateGhostsOfPeerSet(SOSCircleRef circle, SOSPeerInfoRef me) {
47 CFMutableSetRef ghosts = NULL;
48 require_quiet(me, errOut);
49 require_quiet(sosGhostCheckValid(me), errOut);
50 require_quiet(circle, errOut);
51 require_quiet(SOSPeerInfoSerialNumberIsSet(me), errOut);
52 CFStringRef mySerial = SOSPeerInfoCopySerialNumber(me);
53 require_quiet(mySerial, errOut);
54 CFStringRef myPeerID = SOSPeerInfoGetPeerID(me);
55 ghosts = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
56 require_quiet(ghosts, errOut1);
57 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef pi) {
58 CFStringRef theirPeerID = SOSPeerInfoGetPeerID(pi);
59 if(!CFEqual(myPeerID, theirPeerID)) {
60 CFStringRef piSerial = SOSPeerInfoCopySerialNumber(pi);
61 if(CFEqualSafe(mySerial, piSerial)) {
62 CFSetAddValue(ghosts, theirPeerID);
64 CFReleaseNull(piSerial);
68 CFReleaseNull(mySerial);
74 static CFSetRef SOSTrustedCircleCreateGhostsOfPeerSet(SOSAccount* account, SOSPeerInfoRef pi) {
75 return (account) ? SOSCircleCreateGhostsOfPeerSet([account.trust getCircle:NULL], pi): NULL;
78 static CFSetRef SOSTrustedCircleCopyGhostSet(SOSAccount* account) {
79 CFSetRef ghosts = NULL;
80 require_quiet(account, errOut);
81 SOSPeerInfoRef me = account.peerInfo;
82 require_quiet(me, errOut);
83 require_quiet(sosGhostCheckValid(me), errOut);
84 ghosts = SOSTrustedCircleCreateGhostsOfPeerSet(account, me);
90 static CFIndex SOSTrustedCircleGhostSetCount(SOSAccount* account) {
92 CFSetRef ghosts = SOSTrustedCircleCopyGhostSet(account);
93 require_quiet(ghosts, retOut);
94 retval = CFSetGetCount(ghosts);
95 CFReleaseNull(ghosts);
100 bool SOSAccountTrustedCircleHasNoGhostOfMe(SOSAccount* account) {
101 return SOSTrustedCircleGhostSetCount(account) == 0;
104 bool SOSAccountGhostResultsInReset(SOSAccount* account) {
105 return SOSTrustedCircleGhostSetCount(account) == SOSCircleCountPeers([account.trust getCircle:NULL]);
109 // This only works if you're in the circle and have the private key
111 CF_RETURNS_RETAINED SOSCircleRef SOSAccountCloneCircleWithoutMyGhosts(SOSAccount* account, SOSCircleRef startCircle) {
112 SOSCircleRef newCircle = NULL;
113 CFSetRef ghosts = NULL;
114 require_quiet(account, retOut);
115 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, NULL);
116 require_quiet(userPrivKey, retOut);
117 SOSPeerInfoRef me = account.peerInfo;
118 require_quiet(me, retOut);
119 bool iAmApplicant = SOSCircleHasApplicant(startCircle, me, NULL);
121 ghosts = SOSCircleCreateGhostsOfPeerSet(startCircle, me);
122 require_quiet(ghosts, retOut);
123 require_quiet(CFSetGetCount(ghosts), retOut);
125 CFStringSetPerformWithDescription(ghosts, ^(CFStringRef description) {
126 secnotice("ghostbust", "Removing peers: %@", description);
129 newCircle = SOSCircleCopyCircle(kCFAllocatorDefault, startCircle, NULL);
130 require_quiet(newCircle, retOut);
132 if(SOSCircleRemovePeersByIDUnsigned(newCircle, ghosts) && (SOSCircleCountPeers(newCircle) == 0)) {
133 secnotice("resetToOffering", "Reset to offering with last ghost and me as applicant");
134 if(!SOSCircleResetToOffering(newCircle, userPrivKey, account.fullPeerInfo, NULL) ||
135 ![account.trust addiCloudIdentity:newCircle key:userPrivKey err:NULL]){
136 CFReleaseNull(newCircle);
138 account.notifyBackupOnExit = true;
140 CFReleaseNull(newCircle);
143 SOSCircleRemovePeersByID(newCircle, userPrivKey, account.fullPeerInfo, ghosts, NULL);
146 CFReleaseNull(ghosts);