2 // SOSAccountTransaction.c
7 #include "SOSAccountTransaction.h"
9 #include <utilities/SecCFWrappers.h>
10 #include <CoreFoundation/CoreFoundation.h>
12 #include <Security/SecureObjectSync/SOSAccount.h>
13 #include <Security/SecureObjectSync/SOSAccountPriv.h>
14 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
15 #include <Security/SecureObjectSync/SOSTransport.h>
16 #include <Security/SecureObjectSync/SOSTransportCircle.h>
18 #define kPublicKeyNotAvailable "com.apple.security.publickeynotavailable"
20 CFGiblisFor(SOSAccountTransaction
);
22 static void SOSAccountTransactionDestroy(CFTypeRef aObj
) {
23 SOSAccountTransactionRef at
= (SOSAccountTransactionRef
) aObj
;
25 CFReleaseNull(at
->initialUnsyncedViews
);
26 CFReleaseNull(at
->initialID
);
27 CFReleaseNull(at
->account
);
28 CFReleaseNull(at
->initialViews
);
29 CFReleaseNull(at
->initialKeyParameters
);
30 CFReleaseNull(at
->peersToRequestSync
);
33 static CFStringRef
SOSAccountTransactionCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
34 SOSAccountTransactionRef at
= (SOSAccountTransactionRef
) aObj
;
36 CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
38 CFStringAppendFormat(description
, NULL
, CFSTR("<SOSAccountTransactionRef@%p %ld>"),
39 at
, at
->initialViews
? CFSetGetCount(at
->initialViews
) : 0);
44 static void SOSAccountTransactionRestart(SOSAccountTransactionRef txn
) {
45 txn
->initialInCircle
= SOSAccountIsInCircle(txn
->account
, NULL
);
48 txn
->initialTrusted
= (txn
->account
)->user_public_trusted
;
50 if (txn
->initialInCircle
) {
51 SOSAccountEnsureSyncChecking(txn
->account
);
54 CFAssignRetained(txn
->initialUnsyncedViews
, SOSAccountCopyOutstandingViews(txn
->account
));
56 CFReleaseNull(txn
->initialKeyParameters
);
58 if(txn
->account
&& txn
->account
->user_key_parameters
){
59 CFReleaseNull(txn
->initialKeyParameters
);
60 txn
->initialKeyParameters
= CFDataCreateCopy(kCFAllocatorDefault
, txn
->account
->user_key_parameters
);
62 SOSPeerInfoRef mpi
= SOSAccountGetMyPeerInfo(txn
->account
);
63 CFAssignRetained(txn
->initialViews
, mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
);
65 CFRetainAssign(txn
->initialID
, SOSPeerInfoGetPeerID(mpi
));
67 CFReleaseNull(txn
->peersToRequestSync
);
69 CFStringSetPerformWithDescription(txn
->initialViews
, ^(CFStringRef description
) {
70 secnotice("acct-txn", "Starting as:%s v:%@", txn
->initialInCircle
? "member" : "non-member", description
);
75 SOSAccountTransactionRef
SOSAccountTransactionCreate(SOSAccountRef account
) {
76 SOSAccountTransactionRef at
= CFTypeAllocate(SOSAccountTransaction
, struct __OpaqueSOSAccountTransaction
, kCFAllocatorDefault
);
78 at
->account
= CFRetainSafe(account
);
80 at
->initialInCircle
= false;
81 at
->initialViews
= NULL
;
82 at
->initialKeyParameters
= NULL
;
83 at
->initialTrusted
= false;
84 at
->initialUnsyncedViews
= NULL
;
86 at
->peersToRequestSync
= NULL
;
88 SOSAccountTransactionRestart(at
);
93 #define ACCOUNT_STATE_INTERVAL 20
95 void SOSAccountTransactionFinish(SOSAccountTransactionRef txn
) {
96 CFErrorRef localError
= NULL
;
97 bool notifyEngines
= false;
98 static int do_account_state_at_zero
= 0;
100 SOSPeerInfoRef mpi
= SOSAccountGetMyPeerInfo(txn
->account
);
102 bool isInCircle
= SOSAccountIsInCircle(txn
->account
, NULL
);
104 if (isInCircle
&& txn
->peersToRequestSync
) {
105 SOSCCRequestSyncWithPeers(txn
->peersToRequestSync
);
107 CFReleaseNull(txn
->peersToRequestSync
);
110 SOSAccountEnsureSyncChecking(txn
->account
);
112 SOSAccountCancelSyncChecking(txn
->account
);
115 // If our identity changed our inital set should be everything.
116 if (!CFEqualSafe(txn
->initialID
, SOSPeerInfoGetPeerID(mpi
))) {
117 CFAssignRetained(txn
->initialUnsyncedViews
, SOSViewCopyViewSet(kViewSetAll
));
120 CFSetRef finalUnsyncedViews
= SOSAccountCopyOutstandingViews(txn
->account
);
121 if (!CFEqualSafe(txn
->initialUnsyncedViews
, finalUnsyncedViews
)) {
122 if (SOSAccountHandleOutOfSyncUpdate(txn
->account
, txn
->initialUnsyncedViews
, finalUnsyncedViews
)) {
123 notifyEngines
= true;
126 CFStringSetPerformWithDescription(txn
->initialUnsyncedViews
, ^(CFStringRef newUnsyncedDescripion
) {
127 CFStringSetPerformWithDescription(finalUnsyncedViews
, ^(CFStringRef unsyncedDescription
) {
128 secnotice("initial-sync", "Unsynced was: %@", unsyncedDescription
);
129 secnotice("initial-sync", "Unsynced is: %@", newUnsyncedDescripion
);
133 CFReleaseNull(finalUnsyncedViews
);
135 if (txn
->account
->engine_peer_state_needs_repair
) {
136 // We currently only get here from a failed syncwithallpeers, so
137 // that will retry. If this logic changes, force a syncwithallpeers
138 if (!SOSAccountEnsurePeerRegistration(txn
->account
, &localError
)) {
139 secerror("Ensure peer registration while repairing failed: %@", localError
);
141 CFReleaseNull(localError
);
143 notifyEngines
= true;
146 if(txn
->account
->circle_rings_retirements_need_attention
){
147 SOSAccountRecordRetiredPeersInCircle(txn
->account
);
149 SOSAccountEnsureRecoveryRing(txn
->account
);
150 SOSAccountEnsureInBackupRings(txn
->account
);
152 CFErrorRef localError
= NULL
;
153 if(!SOSTransportCircleFlushChanges(txn
->account
->circle_transport
, &localError
)) {
154 secerror("flush circle failed %@", localError
);
156 CFReleaseSafe(localError
);
158 notifyEngines
= true;
162 SOSAccountNotifyEngines(txn
->account
);
165 if(txn
->account
->key_interests_need_updating
){
166 SOSUpdateKeyInterest(txn
->account
);
169 txn
->account
->key_interests_need_updating
= false;
170 txn
->account
->circle_rings_retirements_need_attention
= false;
171 txn
->account
->engine_peer_state_needs_repair
= false;
173 SOSAccountFlattenToSaveBlock(txn
->account
);
175 // Refresh isInCircle since we could have changed our mind
176 isInCircle
= SOSAccountIsInCircle(txn
->account
, NULL
);
178 mpi
= SOSAccountGetMyPeerInfo(txn
->account
);
179 CFSetRef views
= mpi
? SOSPeerInfoCopyEnabledViews(mpi
) : NULL
;
181 CFStringSetPerformWithDescription(views
, ^(CFStringRef description
) {
182 secnotice("acct-txn", "Finished as:%s v:%@", isInCircle
? "member" : "non-member", description
);
184 if(!CFEqualSafe(txn
->initialViews
, views
) || txn
->initialInCircle
!= isInCircle
) {
185 notify_post(kSOSCCViewMembershipChangedNotification
);
186 do_account_state_at_zero
= 0;
189 if((txn
->initialTrusted
!= (txn
->account
)->user_public_trusted
) || (!CFEqualSafe(txn
->initialKeyParameters
, txn
->account
->user_key_parameters
))){
190 notify_post(kPublicKeyNotAvailable
);
191 do_account_state_at_zero
= 0;
194 if(do_account_state_at_zero
<= 0) {
195 SOSAccountLogState(txn
->account
);
196 SOSAccountLogViewState(txn
->account
);
197 do_account_state_at_zero
= ACCOUNT_STATE_INTERVAL
;
199 do_account_state_at_zero
--;
201 CFReleaseNull(views
);
205 void SOSAccountTransactionFinishAndRestart(SOSAccountTransactionRef txn
) {
206 SOSAccountTransactionFinish(txn
);
207 SOSAccountTransactionRestart(txn
);
210 void SOSAccountTransactionAddSyncRequestForPeerID(SOSAccountTransactionRef txn
, CFStringRef peerID
) {
211 if (!txn
->peersToRequestSync
) {
212 txn
->peersToRequestSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
215 CFSetAddValue(txn
->peersToRequestSync
, peerID
);
218 void SOSAccountTransactionAddSyncRequestForAllPeerIDs(SOSAccountTransactionRef txn
, CFSetRef
/* CFStringRef */ peerIDs
) {
219 if (!txn
->peersToRequestSync
) {
220 txn
->peersToRequestSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
223 CFSetUnion(txn
->peersToRequestSync
, peerIDs
);