]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountViewSync.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountViewSync.c
CommitLineData
fa7225c8
A
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
22static 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
37CFMutableSetRef SOSAccountCopyOutstandingViews(SOSAccountRef account) {
38 CFSetRef initialSyncViews = SOSViewCopyViewSet(kViewSetAll);
39 CFMutableSetRef result = SOSAccountCopyIntersectionWithOustanding(account, initialSyncViews);
40 CFReleaseNull(initialSyncViews);
41 return result;
42}
43
44
45bool 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
61done:
62 return isOutstandingView;
63}
64
65CFMutableSetRef 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
85done:
86 if (result == NULL) {
87 result = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, inSet);
88 }
89
90 return result;
91}
92
93bool SOSAccountIntersectsWithOutstanding(SOSAccountRef account, CFSetRef views) {
94 CFSetRef nonInitiallySyncedViews = SOSAccountCopyIntersectionWithOustanding(account, views);
95 bool intersects = !CFSetIsEmpty(nonInitiallySyncedViews);
96 CFReleaseNull(nonInitiallySyncedViews);
97 return intersects;
98}
99
100bool 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
115done:
116 return hasOutstandingViews;
117}
118
119
120//
121// MARK: Initial sync functions
122//
123
124static 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
132bool SOSAccountHasCompletedInitialSync(SOSAccountRef account) {
133 return SOSAccountHasCompletedInitialySyncWithSetKind(account, kViewSetInitial);
134}
135
136bool SOSAccountHasCompletedRequiredBackupSync(SOSAccountRef account) {
137 return SOSAccountHasCompletedInitialySyncWithSetKind(account, kViewSetRequiredForBackup);
138}
139
140
141
142
143//
144// MARK: Handling initial sync being done
145//
146
147static 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
163static 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
177static void SOSAccountHandleRequiredBackupSyncDone(SOSAccountRef account) {
178 secnotice("initial-sync", "Handling Required Backup Sync done");
179}
180
181static 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//
195static CFStringRef CreateUUIDString() {
196 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
197 CFStringRef result = CFUUIDCreateString(kCFAllocatorDefault, uuid);
198 CFReleaseNull(uuid);
199 return result;
200}
201
202CFStringRef 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
222bool 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
230static 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
241static bool CFSetIntersectionWentEmpty(CFSetRef interestingSet, CFSetRef before, CFSetRef after) {
242 return ((before != NULL) && !CFSetIntersectionIsEmpty(interestingSet, before)) &&
243 ((after == NULL) || CFSetIntersectionIsEmpty(interestingSet, after));
244}
245
246static 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
253bool 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
268void 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
334void 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
342static SOSEngineRef SOSAccountGetDataSourceEngine(SOSAccountRef account) {
343 return SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
344}
345
346void 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
365void 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
6b200bc3
A
380bool SOSAccountCheckForAlwaysOnViews(SOSAccountRef account) {
381 bool changed = false;
382 SOSPeerInfoRef myPI = SOSAccountGetMyPeerInfo(account);
383 require_quiet(myPI, done);
384 require_quiet(SOSAccountIsInCircle(account, NULL), done);
385 require_quiet(SOSAccountHasCompletedInitialSync(account), done);
386 CFMutableSetRef viewsToEnsure = SOSViewCopyViewSet(kViewSetAlwaysOn);
387 // Previous version PeerInfo if we were syncing legacy keychain, ensure we include those legacy views.
388 if(!SOSPeerInfoVersionIsCurrent(myPI)) {
389 CFSetRef V0toAdd = SOSViewCopyViewSet(kViewSetV0);
390 CFSetUnion(viewsToEnsure, V0toAdd);
391 CFReleaseNull(V0toAdd);
392 }
393 changed = SOSAccountUpdateFullPeerInfo(account, viewsToEnsure, SOSViewsGetV0ViewSet()); // We don't permit V0 view proper, only sub-views
394 CFReleaseNull(viewsToEnsure);
395done:
396 return changed;
397}
398