]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountViewSync.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountViewSync.c
1 //
2 // SOSAccountViews.c
3 // sec
4 //
5 // Created by Mitch Adler on 6/10/16.
6 //
7 //
8
9
10 #include <CoreFoundation/CoreFoundation.h>
11
12 #include <Security/SecureObjectSync/SOSAccount.h>
13 #include "SOSViews.h"
14 #include "SOSAccountPriv.h"
15
16 #include <utilities/SecCFWrappers.h>
17
18 //
19 // MARK: Helpers
20 //
21
22 static CFMutableSetRef SOSAccountCopyOtherPeersViews(SOSAccountRef account) {
23 __block CFMutableSetRef otherPeersViews = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
24 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
25 SOSPeerInfoWithEnabledViewSet(peer, ^(CFSetRef enabled) {
26 CFSetUnion(otherPeersViews, enabled);
27 });
28 });
29
30 return otherPeersViews;
31 }
32
33 //
34 // MARK: Outstanding tracking
35 //
36
37 CFMutableSetRef SOSAccountCopyOutstandingViews(SOSAccountRef account) {
38 CFSetRef initialSyncViews = SOSViewCopyViewSet(kViewSetAll);
39 CFMutableSetRef result = SOSAccountCopyIntersectionWithOustanding(account, initialSyncViews);
40 CFReleaseNull(initialSyncViews);
41 return result;
42 }
43
44
45 bool SOSAccountIsViewOutstanding(SOSAccountRef account, CFStringRef view) {
46 bool isOutstandingView;
47
48 require_action_quiet(SOSAccountIsInCircle(account, NULL), done, isOutstandingView = true);
49
50 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
51 require_action_quiet(unsyncedObject, done, isOutstandingView = false);
52
53 CFBooleanRef unsyncedBool = asBoolean(unsyncedObject, NULL);
54 if (unsyncedBool) {
55 isOutstandingView = CFBooleanGetValue(unsyncedBool);
56 } else {
57 CFSetRef unsyncedSet = asSet(unsyncedObject, NULL);
58 isOutstandingView = unsyncedSet && CFSetContainsValue(unsyncedSet, view);
59 }
60
61 done:
62 return isOutstandingView;
63 }
64
65 CFMutableSetRef SOSAccountCopyIntersectionWithOustanding(SOSAccountRef account, CFSetRef inSet) {
66 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
67 CFMutableSetRef result = NULL;
68
69 require_quiet(SOSAccountIsInCircle(account, NULL), done);
70
71 CFBooleanRef unsyncedBool = asBoolean(unsyncedObject, NULL);
72 if (unsyncedBool) {
73 if (!CFBooleanGetValue(unsyncedBool)) {
74 result = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
75 }
76 } else {
77 CFSetRef unsyncedSet = asSet(unsyncedObject, NULL);
78 if (unsyncedSet) {
79 result = CFSetCreateIntersection(kCFAllocatorDefault, unsyncedSet, inSet);
80 } else {
81 result = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
82 }
83 }
84
85 done:
86 if (result == NULL) {
87 result = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, inSet);
88 }
89
90 return result;
91 }
92
93 bool SOSAccountIntersectsWithOutstanding(SOSAccountRef account, CFSetRef views) {
94 CFSetRef nonInitiallySyncedViews = SOSAccountCopyIntersectionWithOustanding(account, views);
95 bool intersects = !CFSetIsEmpty(nonInitiallySyncedViews);
96 CFReleaseNull(nonInitiallySyncedViews);
97 return intersects;
98 }
99
100 bool SOSAccountHasOustandingViews(SOSAccountRef account) {
101 bool hasOutstandingViews;
102
103 require_action_quiet(SOSAccountIsInCircle(account, NULL), done, hasOutstandingViews = true);
104
105 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
106 require_action_quiet(unsyncedObject, done, hasOutstandingViews = false);
107
108 CFBooleanRef unsyncedBool = asBoolean(unsyncedObject, NULL);
109 if (unsyncedBool) {
110 hasOutstandingViews = CFBooleanGetValue(unsyncedBool);
111 } else {
112 hasOutstandingViews = isSet(unsyncedBool);
113 }
114
115 done:
116 return hasOutstandingViews;
117 }
118
119
120 //
121 // MARK: Initial sync functions
122 //
123
124 static bool SOSAccountHasCompletedInitialySyncWithSetKind(SOSAccountRef account, ViewSetKind setKind) {
125 CFSetRef viewSet = SOSViewCopyViewSet(setKind);
126 bool completedSync = !SOSAccountIntersectsWithOutstanding(account, viewSet);
127 CFReleaseNull(viewSet);
128
129 return completedSync;
130 }
131
132 bool SOSAccountHasCompletedInitialSync(SOSAccountRef account) {
133 return SOSAccountHasCompletedInitialySyncWithSetKind(account, kViewSetInitial);
134 }
135
136 bool SOSAccountHasCompletedRequiredBackupSync(SOSAccountRef account) {
137 return SOSAccountHasCompletedInitialySyncWithSetKind(account, kViewSetRequiredForBackup);
138 }
139
140
141
142
143 //
144 // MARK: Handling initial sync being done
145 //
146
147 static bool SOSAccountResolvePendingViewSets(SOSAccountRef account, CFErrorRef *error) {
148 bool status = SOSAccountUpdateViewSets(account,
149 asSet(SOSAccountGetValue(account, kSOSPendingEnableViewsToBeSetKey, NULL), NULL),
150 asSet(SOSAccountGetValue(account, kSOSPendingDisableViewsToBeSetKey, NULL), NULL));
151 if(status){
152 SOSAccountClearValue(account, kSOSPendingEnableViewsToBeSetKey, NULL);
153 SOSAccountClearValue(account, kSOSPendingDisableViewsToBeSetKey, NULL);
154
155 secnotice("views","updated view sets!");
156 }
157 else{
158 secerror("Could not update view sets");
159 }
160 return status;
161 }
162
163 static void SOSAccountCallInitialSyncBlocks(SOSAccountRef account) {
164 CFDictionaryRef syncBlocks = NULL;
165 CFTransferRetained(syncBlocks, account->waitForInitialSync_blocks);
166
167 if (syncBlocks) {
168 CFDictionaryForEach(syncBlocks, ^(const void *key, const void *value) {
169 secnotice("updates", "calling in sync block [%@]", key);
170 ((SOSAccountWaitForInitialSyncBlock)value)(account);
171 });
172 }
173 CFReleaseNull(syncBlocks);
174 }
175
176
177 static void SOSAccountHandleRequiredBackupSyncDone(SOSAccountRef account) {
178 secnotice("initial-sync", "Handling Required Backup Sync done");
179 }
180
181 static void SOSAccountHandleInitialSyncDone(SOSAccountRef account) {
182 secnotice("initial-sync", "Handling initial sync done.");
183
184 if(!SOSAccountResolvePendingViewSets(account, NULL))
185 secnotice("initial-sync", "Account could not add the pending view sets");
186
187 SOSAccountCallInitialSyncBlocks(account);
188 }
189
190
191
192 //
193 // MARK: Waiting for in-sync
194 //
195 static CFStringRef CreateUUIDString() {
196 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
197 CFStringRef result = CFUUIDCreateString(kCFAllocatorDefault, uuid);
198 CFReleaseNull(uuid);
199 return result;
200 }
201
202 CFStringRef SOSAccountCallWhenInSync(SOSAccountRef account, SOSAccountWaitForInitialSyncBlock syncBlock) {
203 //if we are not initially synced
204 CFStringRef id = NULL;
205 CFTypeRef unSyncedViews = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
206 if (unSyncedViews != NULL) {
207 id = CreateUUIDString();
208 secnotice("initial-sync", "adding sync block [%@] to array!", id);
209 SOSAccountWaitForInitialSyncBlock copy = Block_copy(syncBlock);
210 if (account->waitForInitialSync_blocks == NULL) {
211 account->waitForInitialSync_blocks = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
212 }
213 CFDictionarySetValue(account->waitForInitialSync_blocks, id, copy);
214 Block_release(copy);
215 } else {
216 syncBlock(account);
217 }
218
219 return id;
220 }
221
222 bool SOSAccountUnregisterCallWhenInSync(SOSAccountRef account, CFStringRef id) {
223 if (account->waitForInitialSync_blocks == NULL) return false;
224
225 bool removed = CFDictionaryGetValueIfPresent(account->waitForInitialSync_blocks, id, NULL);
226 CFDictionaryRemoveValue(account->waitForInitialSync_blocks, id);
227 return removed;
228 }
229
230 static void performWithInitialSyncDescription(CFTypeRef object, void (^action)(CFStringRef description)) {
231 CFSetRef setObject = asSet(object, NULL);
232 if (setObject) {
233 CFStringSetPerformWithDescription(setObject, action);
234 } else {
235 CFStringRef format = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), object);
236 action(format);
237 CFReleaseNull(format);
238 }
239 }
240
241 static bool CFSetIntersectionWentEmpty(CFSetRef interestingSet, CFSetRef before, CFSetRef after) {
242 return ((before != NULL) && !CFSetIntersectionIsEmpty(interestingSet, before)) &&
243 ((after == NULL) || CFSetIntersectionIsEmpty(interestingSet, after));
244 }
245
246 static bool SOSViewIntersectionWentEmpty(ViewSetKind kind, CFSetRef before, CFSetRef after) {
247 CFSetRef kindSet = SOSViewCopyViewSet(kind);
248 bool result = CFSetIntersectionWentEmpty(kindSet, before, after);
249 CFReleaseNull(kindSet);
250 return result;
251 }
252
253 bool SOSAccountHandleOutOfSyncUpdate(SOSAccountRef account, CFSetRef oldOOSViews, CFSetRef newOOSViews) {
254 bool actionTaken = false;
255
256 if (SOSViewIntersectionWentEmpty(kViewSetInitial, oldOOSViews, newOOSViews)) {
257 SOSAccountHandleInitialSyncDone(account);
258 actionTaken = true;
259 }
260
261 if (SOSViewIntersectionWentEmpty(kViewSetRequiredForBackup, oldOOSViews, newOOSViews)) {
262 SOSAccountHandleRequiredBackupSyncDone(account);
263 actionTaken = true;
264 }
265 return actionTaken;
266 }
267
268 void SOSAccountUpdateOutOfSyncViews(SOSAccountTransactionRef aTxn, CFSetRef viewsInSync) {
269 SOSAccountRef account = aTxn->account;
270 SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
271 bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
272
273 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
274 __block CFTypeRef newUnsyncedObject = CFRetainSafe(unsyncedObject);
275
276 CFSetRef unsyncedSet = NULL;
277 CFMutableSetRef newUnsyncedSet = NULL;
278
279 performWithInitialSyncDescription(viewsInSync, ^(CFStringRef viewsInSyncDescription) {
280 secnotice("initial-sync", "Views in sync: %@", viewsInSyncDescription);
281 });
282
283
284 if (!inOrApplying) {
285 if (unsyncedObject != NULL) {
286 secnotice("initial-sync", "not in circle nor applying: clearing pending");
287 CFReleaseNull(newUnsyncedObject);
288 }
289 } else if (circleStatus == kSOSCCInCircle) {
290 if (unsyncedObject == kCFBooleanTrue) {
291 unsyncedSet = SOSViewCopyViewSet(kViewSetAll);
292 CFAssignRetained(newUnsyncedObject, CFSetCreateCopy(kCFAllocatorDefault, unsyncedSet));
293
294 secnotice("initial-sync", "Pending views setting to all we can expect.");
295 } else if (isSet(unsyncedObject)) {
296 unsyncedSet = (CFSetRef) CFRetainSafe(unsyncedObject);
297 }
298
299 if (unsyncedSet) {
300 CFSetRef otherPeersViews = SOSAccountCopyOtherPeersViews(account);
301
302 newUnsyncedSet = CFSetCreateIntersection(kCFAllocatorDefault, unsyncedSet, otherPeersViews);
303
304 if (viewsInSync) {
305 CFSetSubtract(newUnsyncedSet, viewsInSync);
306 }
307
308 CFRetainAssign(newUnsyncedObject, newUnsyncedSet);
309 CFReleaseNull(otherPeersViews);
310 }
311
312 performWithInitialSyncDescription(newUnsyncedSet, ^(CFStringRef unsynced) {
313 secnotice("initial-sync", "Unsynced: %@", unsynced);
314 });
315 }
316
317
318 if (isSet(newUnsyncedObject) && CFSetIsEmpty((CFSetRef) newUnsyncedObject)) {
319 secnotice("initial-sync", "Empty set, using NULL instead");
320 CFReleaseNull(newUnsyncedObject);
321 }
322
323 CFErrorRef localError = NULL;
324 if (!SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newUnsyncedObject, &localError)) {
325 secnotice("initial-sync", "Failure saving new unsynced value: %@ value: %@", localError, newUnsyncedObject);
326 }
327 CFReleaseNull(localError);
328
329 CFReleaseNull(newUnsyncedObject);
330 CFReleaseNull(newUnsyncedSet);
331 CFReleaseNull(unsyncedSet);
332 }
333
334 void SOSAccountPeerGotInSync(SOSAccountTransactionRef aTxn, CFStringRef peerID, CFSetRef views) {
335 SOSAccountRef account = aTxn->account;
336 secnotice("initial-sync", "Peer %@ synced views: %@", peerID, views);
337 if (account->trusted_circle && SOSAccountIsInCircle(account, NULL) && SOSCircleHasActivePeerWithID(account->trusted_circle, peerID, NULL)) {
338 SOSAccountUpdateOutOfSyncViews(aTxn, views);
339 }
340 }
341
342 static SOSEngineRef SOSAccountGetDataSourceEngine(SOSAccountRef account) {
343 return SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
344 }
345
346 void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
347 if (!account->isListeningForSync) {
348 SOSEngineRef engine = SOSAccountGetDataSourceEngine(account);
349
350 if (engine) {
351 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
352 SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
353 SOSEngineSetSyncCompleteListener(engine, ^(CFStringRef peerID, CFSetRef views) {
354 SOSAccountWithTransaction_Locked(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) {
355 SOSAccountPeerGotInSync(txn, peerID, views);
356 });
357 });
358 account->isListeningForSync = true;
359 } else {
360 secerror("Couldn't find engine to setup notifications!!!");
361 }
362 }
363 }
364
365 void SOSAccountCancelSyncChecking(SOSAccountRef account) {
366 if (account->isListeningForSync) {
367 SOSEngineRef engine = SOSAccountGetDataSourceEngine(account);
368
369 if (engine) {
370 secnotice("initial-sync", "Cancelling notifications to monitor in-sync");
371 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
372 SOSEngineSetSyncCompleteListener(engine, NULL);
373 } else {
374 secnotice("initial-sync", "No engine to cancel notification from.");
375 }
376 account->isListeningForSync = false;
377 }
378 }
379