]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57740.20.22.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5 /*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10 #include "SOSAccountPriv.h"
11 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
12 #include <Security/SecureObjectSync/SOSTransportCircle.h>
13 #include <Security/SecureObjectSync/SOSTransportMessage.h>
14 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
15 #include <Security/SecureObjectSync/SOSKVSKeys.h>
16 #include <Security/SecureObjectSync/SOSTransport.h>
17 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
18 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
19 #include <Security/SecureObjectSync/SOSEngine.h>
20 #include <Security/SecureObjectSync/SOSPeerCoder.h>
21 #include <Security/SecureObjectSync/SOSInternal.h>
22 #include <Security/SecureObjectSync/SOSRing.h>
23 #include <Security/SecureObjectSync/SOSRingUtils.h>
24 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
25 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
26 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
27 #include <Security/SecItemInternal.h>
28 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
29 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
30
31 #include <utilities/SecCFWrappers.h>
32
33 CFGiblisWithCompareFor(SOSAccount);
34
35 const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
36 const CFStringRef SOSTransportMessageTypeIDSV2 = CFSTR("IDS2.0");
37 const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
38 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
39 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
40 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
41 const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
42 const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
43
44
45 #define DATE_LENGTH 25
46 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
47
48 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
49 {
50 bool result = false;
51 CFStringRef circle_name = NULL;
52
53 require_quiet(a, xit);
54 require_quiet(a->factory, xit);
55
56 circle_name = SOSDataSourceFactoryCopyName(a->factory);
57 require(circle_name, xit);
58
59 SOSAccountEnsureCircle(a, circle_name, NULL);
60
61 result = true;
62
63 xit:
64 // We don't own name, so don't release it.
65 CFReleaseNull(circle_name);
66 return result;
67 }
68
69
70 SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
71 CFDictionaryRef gestalt,
72 SOSDataSourceFactoryRef factory) {
73 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
74
75 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
76
77 a->gestalt = CFRetainSafe(gestalt);
78
79 a->trusted_circle = NULL;
80 a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
81 a->my_identity = NULL;
82 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
83
84 a->factory = factory; // We adopt the factory. kthanksbai.
85
86 a->isListeningForSync = false;
87
88 a->_user_private = NULL;
89 a->_password_tmp = NULL;
90 a->user_private_timer = NULL;
91 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
92
93 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
94 a->waitForInitialSync_blocks = NULL;
95 a->departure_code = kSOSNeverAppliedToCircle;
96
97 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
98 a->circle_transport = NULL;
99 a->kvs_message_transport = NULL;
100 a->ids_message_transport = NULL;
101 a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
102 SOSAccountAddRingDictionary(a);
103
104 a->saveBlock = NULL;
105 a->circle_rings_retirements_need_attention = false;
106 a->engine_peer_state_needs_repair = false;
107 a->key_interests_need_updating = false;
108 a->deviceID = NULL;
109
110 return a;
111 }
112
113 //
114 // MARK: Transactional
115 //
116
117 void SOSAccountWithTransaction_Locked(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
118 SOSAccountTransactionRef at = SOSAccountTransactionCreate(account);
119 action(account, at);
120 SOSAccountTransactionFinish(at);
121 CFReleaseNull(at);
122 }
123
124
125
126 void SOSAccountWithTransaction(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
127 dispatch_block_t with_transaction = ^{
128 SOSAccountWithTransaction_Locked(account, action);
129 };
130
131 if (sync) {
132 dispatch_sync(SOSAccountGetQueue(account), with_transaction);
133 } else {
134 dispatch_async(SOSAccountGetQueue(account), with_transaction);
135 }
136 }
137
138 void SOSAccountWithTransactionSync(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
139 SOSAccountWithTransaction(account, true, action);
140 }
141
142 void SOSAccountWithTransactionAsync(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
143 SOSAccountWithTransaction(account, false, action);
144 }
145
146 //
147 // MARK: Save Block
148 //
149
150 void SOSAccountSetSaveBlock(SOSAccountRef account, SOSAccountSaveBlock saveBlock) {
151 CFAssignRetained(account->saveBlock, Block_copy(saveBlock));
152 }
153
154 void SOSAccountFlattenToSaveBlock(SOSAccountRef account) {
155 if (account->saveBlock) {
156 CFErrorRef localError = NULL;
157 CFDataRef saveData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &localError);
158
159 (account->saveBlock)(saveData, localError);
160
161 CFReleaseNull(saveData);
162 CFReleaseNull(localError);
163 }
164 }
165
166 //
167 // MARK: Security Properties
168 //
169
170 SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
171 SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
172 bool updateCircle = false;
173 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
174 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
175 retval = SOSFullPeerInfoUpdateSecurityProperty(account->my_identity, actionCode, property, error);
176
177 if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
178 updateCircle = true;
179 } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
180 updateCircle = true;
181 } else if(actionCode == kSOSCCSecurityPropertyPending) {
182 updateCircle = true;
183 }
184
185 if (updateCircle) {
186 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
187 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
188 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
189 });
190 }
191
192 errOut:
193 return retval;
194 }
195
196 SOSSecurityPropertyResultCode SOSAccountSecurityPropertyStatus(SOSAccountRef account, CFStringRef property, CFErrorRef *error) {
197 SOSSecurityPropertyResultCode retval = kSOSCCGeneralViewError;
198 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
199 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
200 retval = SOSFullPeerInfoSecurityPropertyStatus(account->my_identity, property, error);
201 errOut:
202 return retval;
203 }
204
205 bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
206 {
207 if (CFEqualSafe(new_gestalt, account->gestalt))
208 return false;
209
210 if (account->trusted_circle && account->my_identity
211 && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
212 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
213 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
214 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
215 });
216 }
217
218 CFRetainAssign(account->gestalt, new_gestalt);
219 return true;
220 }
221
222 static bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
223 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
224 //send new DSID over account changed
225 SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
226 return true;
227 }
228
229 void SOSAccountAssertDSID(SOSAccountRef account, CFStringRef dsid) {
230 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
231 if(accountDSID == NULL) {
232 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
233
234 SOSAccountUpdateDSID(account, dsid);
235 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
236 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
237
238 //DSID has changed, blast the account!
239 SOSAccountSetToNew(account);
240
241 //update DSID to the new DSID
242 SOSAccountUpdateDSID(account, dsid);
243 } else {
244 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
245 }
246 }
247
248 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews, CFSetRef excludedViews) {
249 if (account->trusted_circle && account->my_identity) {
250 if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
251 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
252 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
253 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
254 });
255 }
256 }
257
258 return true;
259 }
260
261 static bool SOSAccountValueSetContainsValue(SOSAccountRef account, CFStringRef key, CFTypeRef value) {
262 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
263 return foundSet && CFSetContainsValue(foundSet, value);
264 }
265
266 static void SOSAccountValueUnionWith(SOSAccountRef account, CFStringRef key, CFSetRef valuesToUnion) {
267 CFMutableSetRef unionedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, valuesToUnion);
268 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
269 if (foundSet) {
270 CFSetUnion(unionedSet, foundSet);
271 }
272 SOSAccountSetValue(account, key, unionedSet, NULL);
273 CFReleaseNull(unionedSet);
274 }
275
276 static void SOSAccountValueSubtractFrom(SOSAccountRef account, CFStringRef key, CFSetRef valuesToSubtract) {
277 CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
278 if (foundSet) {
279 CFMutableSetRef subtractedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, foundSet);
280 CFSetSubtract(subtractedSet, valuesToSubtract);
281 SOSAccountSetValue(account, key, subtractedSet, NULL);
282 CFReleaseNull(subtractedSet);
283 }
284 }
285
286 void SOSAccountPendEnableViewSet(SOSAccountRef account, CFSetRef enabledViews)
287 {
288 if(CFSetGetValue(enabledViews, kSOSViewKeychainV0) != NULL) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
289
290 SOSAccountValueUnionWith(account, kSOSPendingEnableViewsToBeSetKey, enabledViews);
291 SOSAccountValueSubtractFrom(account, kSOSPendingDisableViewsToBeSetKey, enabledViews);
292 }
293
294
295 void SOSAccountPendDisableViewSet(SOSAccountRef account, CFSetRef disabledViews)
296 {
297 SOSAccountValueUnionWith(account, kSOSPendingDisableViewsToBeSetKey, disabledViews);
298 SOSAccountValueSubtractFrom(account, kSOSPendingEnableViewsToBeSetKey, disabledViews);
299 }
300
301 static SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccountRef account, SOSViewActionCode actionCode) {
302 SOSViewResultCode retval = kSOSCCGeneralViewError;
303 // The V0 view switches on and off all on it's own, we allow people the delusion
304 // of control and status if it's what we're stuck at., otherwise error.
305 if (SOSAccountSyncingV0(account)) {
306 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
307 retval = kSOSCCViewMember;
308 } else {
309 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
310 retval = kSOSCCViewNotMember;
311 }
312 errOut:
313 return retval;
314 }
315
316
317 SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
318 SOSViewResultCode retval = kSOSCCGeneralViewError;
319 SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
320 bool alreadyInSync = SOSAccountHasCompletedInitialSync(account);
321
322 bool updateCircle = false;
323 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
324 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
325 require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
326 currentStatus = SOSAccountViewStatus(account, viewname, error);
327 require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
328
329 if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
330 retval = SOSAccountVirtualV0Behavior(account, actionCode);
331 } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
332 // Subviews of V0 syncing can't be turned off if V0 is on.
333 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
334 retval = kSOSCCViewMember;
335 } else {
336 CFMutableSetRef pendingSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
337 CFSetAddValue(pendingSet, viewname);
338
339 if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
340 if(alreadyInSync) {
341 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
342 if(retval == kSOSCCViewMember) updateCircle = true;
343 } else {
344 SOSAccountPendEnableViewSet(account, pendingSet);
345 retval = kSOSCCViewMember;
346 updateCircle = false;
347 }
348 } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
349 if(alreadyInSync) {
350 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
351 if(retval == kSOSCCViewNotMember) updateCircle = true;
352 } else {
353 SOSAccountPendDisableViewSet(account, pendingSet);
354 retval = kSOSCCViewNotMember;
355 updateCircle = false;
356 }
357 } else {
358 retval = currentStatus;
359 }
360
361 CFReleaseNull(pendingSet);
362
363 if (updateCircle) {
364 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
365 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
366 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
367 });
368 }
369 }
370
371 errOut:
372 return retval;
373 }
374
375 SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewname, CFErrorRef *error) {
376 SOSViewResultCode retval = kSOSCCGeneralViewError;
377 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
378 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
379
380 if (SOSAccountValueSetContainsValue(account, kSOSPendingEnableViewsToBeSetKey, viewname)) {
381 retval = kSOSCCViewMember;
382 } else if (SOSAccountValueSetContainsValue(account, kSOSPendingDisableViewsToBeSetKey, viewname)) {
383 retval = kSOSCCViewNotMember;
384 } else {
385 retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
386 }
387
388 // If that doesn't say we're a member and this view is a V0 subview, and we're syncing V0 views we are a member
389 if (retval != kSOSCCViewMember) {
390 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
391 && SOSAccountSyncingV0(account)) {
392 retval = kSOSCCViewMember;
393 }
394 }
395
396 // If we're only an applicant we report pending if we would be a view member
397 if (retval == kSOSCCViewMember) {
398 bool isApplicant = SOSCircleHasApplicant(account->trusted_circle, SOSAccountGetMyPeerInfo(account), error);
399 if (isApplicant) {
400 retval = kSOSCCViewPending;
401 }
402 }
403
404 errOut:
405 return retval;
406 }
407
408 static void dumpViewSet(CFStringRef label, CFSetRef views) {
409 if(views) {
410 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
411 secnotice("circleChange", "%@ list: %@", label, description);
412 });
413 } else {
414 secnotice("circleChange", "No %@ list provided.", label);
415 }
416 }
417
418 static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account, CFMutableSetRef viewSet, SOSViewActionCode actionCode) {
419 bool retval = true;
420 if(viewSet && CFSetContainsValue(viewSet, kSOSViewKeychainV0)) {
421 retval = SOSAccountVirtualV0Behavior(account, actionCode) != kSOSCCGeneralViewError;
422 CFSetRemoveValue(viewSet, kSOSViewKeychainV0);
423 }
424 return retval;
425 }
426
427 bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef origEnabledViews, CFSetRef origDisabledViews) {
428 bool retval = false;
429 bool updateCircle = false;
430 SOSPeerInfoRef pi = NULL;
431 CFMutableSetRef enabledViews = NULL;
432 CFMutableSetRef disabledViews = NULL;
433 if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
434 if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
435 dumpViewSet(CFSTR("Enabled"), enabledViews);
436 dumpViewSet(CFSTR("Disabled"), disabledViews);
437
438 require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
439
440 // Make sure we have a peerInfo capable of supporting views.
441 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
442 require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
443 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
444
445 pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
446
447 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
448
449 if(!SOSPeerInfoVersionIsCurrent(pi)) {
450 CFErrorRef updateFailure = NULL;
451 require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
452 (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
453 secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
454 updateCircle = true;
455 }
456
457 CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
458 secnotice("viewChange", "Enabling %@", description);
459 });
460
461 CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
462 secnotice("viewChange", "Disabling %@", description);
463 });
464
465 require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
466 require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
467
468 if(SOSAccountHasCompletedInitialSync(account)) {
469 if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
470 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
471 retval = true;
472 } else {
473 //hold on to the views and enable them later
474 if(enabledViews) SOSAccountPendEnableViewSet(account, enabledViews);
475 if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
476 retval = true;
477 }
478
479 if(updateCircle) {
480 /* UPDATE FULLPEERINFO VIEWS */
481 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
482
483 require_quiet(SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
484 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
485 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
486 }), errOut);
487
488 // Make sure we update the engine
489 account->circle_rings_retirements_need_attention = true;
490 }
491
492 errOut:
493 CFReleaseNull(enabledViews);
494 CFReleaseNull(disabledViews);
495 CFReleaseNull(pi);
496 return retval;
497 }
498
499
500 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
501 CFDictionaryRef gestalt,
502 SOSDataSourceFactoryRef factory) {
503 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
504
505 SOSAccountEnsureFactoryCircles(a);
506
507 a->key_interests_need_updating = true;
508
509 return a;
510 }
511
512 static void SOSAccountDestroy(CFTypeRef aObj) {
513 SOSAccountRef a = (SOSAccountRef) aObj;
514
515 // We don't own the factory, merely have a reference to the singleton
516 // Don't free it.
517 // a->factory
518
519 SOSAccountCancelSyncChecking(a);
520
521 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
522
523 if (engine)
524 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
525
526 dispatch_sync(a->queue, ^{
527 CFReleaseNull(a->gestalt);
528
529 CFReleaseNull(a->my_identity);
530 CFReleaseNull(a->trusted_circle);
531 CFReleaseNull(a->backups);
532 CFReleaseNull(a->retirees);
533
534 a->user_public_trusted = false;
535 CFReleaseNull(a->user_public);
536 CFReleaseNull(a->user_key_parameters);
537
538 SOSAccountPurgePrivateCredential(a);
539 CFReleaseNull(a->previous_public);
540 CFReleaseNull(a->_user_private);
541 CFReleaseNull(a->_password_tmp);
542
543 a->departure_code = kSOSNeverAppliedToCircle;
544 CFReleaseNull(a->kvs_message_transport);
545 CFReleaseNull(a->ids_message_transport);
546 CFReleaseNull(a->key_transport);
547 CFReleaseNull(a->circle_transport);
548 dispatch_release(a->queue);
549
550 dispatch_release(a->user_private_timer);
551 CFReleaseNull(a->change_blocks);
552 CFReleaseNull(a->waitForInitialSync_blocks);
553 CFReleaseNull(a->expansion);
554
555 CFReleaseNull(a->saveBlock);
556 CFReleaseNull(a->deviceID);
557 });
558 }
559
560 static OSStatus do_delete(CFDictionaryRef query) {
561 OSStatus result;
562
563 result = SecItemDelete(query);
564 if (result) {
565 secerror("SecItemDelete: %d", (int)result);
566 }
567 return result;
568 }
569
570 static int
571 do_keychain_delete_aks_bags()
572 {
573 OSStatus result;
574 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
575 kSecClass, kSecClassGenericPassword,
576 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
577 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
578 kSecAttrService, CFSTR("SecureBackupService"),
579 kSecAttrSynchronizable, kCFBooleanTrue,
580 kSecUseTombstones, kCFBooleanFalse,
581 NULL);
582
583 result = do_delete(item);
584 CFReleaseSafe(item);
585
586 return result;
587 }
588
589 static int
590 do_keychain_delete_identities()
591 {
592 OSStatus result;
593 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
594 kSecClass, kSecClassKey,
595 kSecAttrSynchronizable, kCFBooleanTrue,
596 kSecUseTombstones, kCFBooleanFalse,
597 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
598 NULL);
599
600 result = do_delete(item);
601 CFReleaseSafe(item);
602
603 return result;
604 }
605
606 static int
607 do_keychain_delete_lakitu()
608 {
609 OSStatus result;
610 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
611 kSecClass, kSecClassGenericPassword,
612 kSecAttrSynchronizable, kCFBooleanTrue,
613 kSecUseTombstones, kCFBooleanFalse,
614 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
615 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
616 kSecAttrService, CFSTR("EscrowService"),
617 NULL);
618
619 result = do_delete(item);
620 CFReleaseSafe(item);
621
622 return result;
623 }
624
625 static int
626 do_keychain_delete_sbd()
627 {
628 OSStatus result;
629 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
630 kSecClass, kSecClassGenericPassword,
631 kSecAttrSynchronizable, kCFBooleanTrue,
632 kSecUseTombstones, kCFBooleanFalse,
633 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
634 NULL);
635
636 result = do_delete(item);
637 CFReleaseSafe(item);
638
639 return result;
640 }
641
642 void static SOSAccountResetKeyInterests(SOSAccountRef a) {
643 CFDictionaryRef emptyDictionary = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
644 SOSCloudKeychainUpdateKeys(emptyDictionary, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error) {
645 if (error) {
646 secerror("Error updating keys: %@", error);
647 }
648 });
649 CFReleaseNull(emptyDictionary);
650 }
651
652 void SOSAccountSetToNew(SOSAccountRef a) {
653 secnotice("accountChange", "Setting Account to New");
654 int result = 0;
655
656 CFReleaseNull(a->my_identity);
657 CFReleaseNull(a->trusted_circle);
658 CFReleaseNull(a->backups);
659 CFReleaseNull(a->retirees);
660
661 CFReleaseNull(a->user_key_parameters);
662 CFReleaseNull(a->user_public);
663 CFReleaseNull(a->previous_public);
664 CFReleaseNull(a->_user_private);
665 CFReleaseNull(a->_password_tmp);
666
667 CFReleaseNull(a->key_transport);
668 CFReleaseNull(a->circle_transport);
669 CFReleaseNull(a->kvs_message_transport);
670 CFReleaseNull(a->ids_message_transport);
671 CFReleaseNull(a->expansion);
672 CFReleaseNull(a->deviceID);
673
674 /* remove all syncable items */
675 result = do_keychain_delete_aks_bags(); (void) result;
676 secdebug("set to new", "result for deleting aks bags: %d", result);
677
678 result = do_keychain_delete_identities(); (void) result;
679 secdebug("set to new", "result for deleting identities: %d", result);
680
681 result = do_keychain_delete_lakitu(); (void) result;
682 secdebug("set to new", "result for deleting lakitu: %d", result);
683
684 result = do_keychain_delete_sbd(); (void) result;
685 secdebug("set to new", "result for deleting sbd: %d", result);
686
687 a->user_public_trusted = false;
688 a->departure_code = kSOSNeverAppliedToCircle;
689
690 if (a->user_private_timer) {
691 dispatch_source_cancel(a->user_private_timer);
692 dispatch_release(a->user_private_timer);
693 a->user_private_timer = NULL;
694 xpc_transaction_end();
695
696 }
697 if (a->lock_notification_token != NOTIFY_TOKEN_INVALID) {
698 notify_cancel(a->lock_notification_token);
699 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
700 }
701
702 // keeping gestalt;
703 // keeping factory;
704 // Live Notification
705 // change_blocks;
706 // update_interest_block;
707 // update_block;
708
709 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
710 a->circle_transport = NULL;
711 a->kvs_message_transport = NULL;
712 a->ids_message_transport = NULL;
713
714 a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
715
716 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
717 a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
718 SOSAccountAddRingDictionary(a);
719
720 SOSAccountEnsureFactoryCircles(a); // Does rings too
721
722 // Reset our key interests since we are new, we need to hear about everything:
723 SOSAccountResetKeyInterests(a);
724
725 a->key_interests_need_updating = true;
726 }
727
728 bool SOSAccountIsNew(SOSAccountRef account, CFErrorRef *error){
729 bool result = false;
730 require_quiet(account->user_public_trusted == false, exit);
731 require_quiet(account->departure_code == kSOSNeverAppliedToCircle, exit);
732 require_quiet(account->user_private_timer == NULL, exit);
733 require_quiet(account->lock_notification_token == NOTIFY_TOKEN_INVALID, exit);
734 require_quiet (CFDictionaryGetCount(account->backups) == 0, exit);
735 require_quiet(CFSetGetCount(account->retirees) == 0, exit);
736
737 result = true;
738 exit:
739 return result;
740 }
741
742 static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
743 SOSAccountRef a = (SOSAccountRef) aObj;
744
745 CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
746
747 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: %c%c%c%c%c G: %@ Me: %@ C: %@ >"), a,
748 a->user_public ? 'P' : 'p',
749 a->user_public_trusted ? 'T' : 't',
750 a->isListeningForSync ? 'L' : 'l',
751 SOSAccountHasCompletedInitialSync(a) ? 'C' : 'c',
752 SOSAccountHasCompletedRequiredBackupSync(a) ? 'B' : 'b',
753 gestaltDescription, a->my_identity, a->trusted_circle);
754
755 CFReleaseNull(gestaltDescription);
756
757 return result;
758 }
759
760 CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
761
762 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
763
764 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
765
766 CFReleaseNull(gestaltDescription);
767
768 return result;
769 }
770
771 static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
772 {
773 SOSAccountRef laccount = (SOSAccountRef) lhs;
774 SOSAccountRef raccount = (SOSAccountRef) rhs;
775
776 return CFEqualSafe(laccount->gestalt, raccount->gestalt)
777 && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
778 && CFEqualSafe(laccount->expansion, raccount->expansion)
779 && CFEqualSafe(laccount->my_identity, raccount->my_identity);
780 }
781
782 dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
783 return account->queue;
784 }
785
786 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
787 account->user_public_trusted = true;
788 }
789
790 SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
791 {
792 return CFRetainSafe(account->my_identity);
793 }
794
795 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
796 bool ok = false;
797 __block CFErrorRef error = NULL;
798
799 if (!SOSAccountHasPublicKey(account, &error)) {
800 CFReleaseSafe(error);
801 return false;
802 }
803
804 bool hasID = true;
805
806 require_action_quiet(account->my_identity, xit,
807 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
808
809 SOSTransportMessageIDSGetIDSDeviceID(account);
810
811 require_action_quiet(account->trusted_circle, xit,
812 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
813
814 require_action_quiet(hasID, xit,
815 SOSCreateError(kSOSErrorBadFormat, CFSTR("Missing IDS device ID"), NULL, &error));
816 ok = SOSCircleHasPeerWithID(account->trusted_circle,
817 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity)), &error);
818 xit:
819 if (!ok) {
820 secerror("sync with device failure: %@", error);
821 }
822 CFReleaseSafe(error);
823 return ok;
824 }
825
826 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef peerID) {
827 SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
828 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
829
830 return myPeerID && CFEqualSafe(myPeerID, peerID);
831 }
832
833 bool SOSAccountSendIKSPSyncList(SOSAccountRef account, CFErrorRef *error){
834 bool result = true;
835 __block CFErrorRef localError = NULL;
836 __block CFMutableArrayRef ids = NULL;
837 SOSCircleRef circle = NULL;
838
839 require_action_quiet(SOSAccountIsInCircle(account, NULL), xit,
840 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"),
841 NULL, &localError));
842
843 circle = SOSAccountGetCircle(account, error);
844 ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
845
846 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
847 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
848 NULL, &localError));
849
850 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
851 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
852 if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer) &&
853 SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
854 SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue);
855 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
856 if(deviceID != NULL){
857 CFArrayAppendValue(ids, deviceID);
858 }
859 CFReleaseNull(deviceID);
860 }
861 }
862 });
863 require_quiet(CFArrayGetCount(ids) != 0, xit);
864 secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids);
865
866 SOSCloudKeychainGetIDSDeviceAvailability(ids, SOSAccountGetMyPeerID(account), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
867 bool success = (sync_error == NULL);
868 if(!success)
869 secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error);
870 });
871 xit:
872 if(error && *error != NULL)
873 secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
874
875 if(localError)
876 secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
877
878 CFReleaseNull(ids);
879 CFReleaseNull(localError);
880
881 return result;
882 }
883
884 bool SOSAccountSyncWithAllKVSPeers(SOSAccountRef account, CFErrorRef *error)
885 {
886 __block bool result = true;
887
888 if(SOSAccountIsInCircle(account, NULL)) {
889 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
890 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
891 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
892 if(deviceID == NULL || !SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
893 result = SOSAccountSyncWithKVSPeer(account, SOSPeerInfoGetPeerID(peer), error);
894 if(result){
895 secnotice("KVS Transport", "synced with peer: %@", SOSPeerInfoGetPeerID(peer));
896 }
897 else{
898 secnotice("KVS Transport", "failed to sync with peer: %@", SOSPeerInfoGetPeerID(peer));
899 }
900 }
901 CFReleaseNull(deviceID);
902 }
903 });
904 }
905 secnotice("sync", "SOSAccountSyncWithAllKVSPeers returns: %d", result);
906 return true;
907 }
908
909 static CFMutableArrayRef SOSAccountCopyPeerIDsForDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef* error) {
910 CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
911
912 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
913 CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
914 if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
915 CFArrayAppendValue(peerIDs, SOSPeerInfoGetPeerID(peer));
916 }
917 CFReleaseNull(peerDeviceID);
918 });
919
920 if (peerIDs == NULL || CFArrayGetCount(peerIDs) == 0) {
921 CFReleaseNull(peerIDs);
922 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer with DSID: %@"), deviceID);
923 }
924
925 return peerIDs;
926 }
927
928 static bool SOSAccountSyncWithKVSPeers(SOSAccountRef account, CFArrayRef peerIDs, CFErrorRef *error) {
929 CFDictionaryRef circleToPeerIDs = NULL;
930
931 CFErrorRef localError = NULL;
932 bool result = false;
933 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
934 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
935 NULL, &localError));
936
937 circleToPeerIDs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
938 SOSCircleGetName(account->trusted_circle), peerIDs,
939 NULL);
940 result = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
941
942 SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
943 result &= SOSEngineSyncWithPeers(engine, &localError);
944
945 if (result)
946 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
947
948 xit:
949 CFReleaseNull(circleToPeerIDs);
950 if (!result) {
951 // Tell account to update SOSEngine with current trusted peers
952 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
953 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
954 account->engine_peer_state_needs_repair = true;
955 }
956 CFErrorPropagate(localError, error);
957 localError = NULL;
958 }
959 return result;
960
961 }
962
963 bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error) {
964 bool result = false;
965 CFErrorRef localError = NULL;
966
967 secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID);
968
969 CFArrayRef peerIDs = SOSAccountCopyPeerIDsForDSID(account, deviceID, &localError);
970 require_quiet(peerIDs, xit);
971
972 CFStringArrayPerfromWithDescription(peerIDs, ^(CFStringRef peerIDList) {
973 secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList);
974 });
975
976 result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
977 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
978
979 xit:
980 CFReleaseNull(peerIDs);
981 CFErrorPropagate(localError, error);
982
983 return result;
984 }
985
986 bool SOSAccountSyncWithKVSPeer(SOSAccountRef account, CFStringRef peerID, CFErrorRef *error)
987 {
988 bool result = false;
989 CFErrorRef localError = NULL;
990
991 secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID);
992
993 CFArrayRef peerIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault, peerID, NULL);
994
995 result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
996 secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
997
998 CFReleaseNull(peerIDs);
999 CFErrorPropagate(localError, error);
1000
1001 return result;
1002 }
1003
1004 #define LOG_ENGINE_STATE_INTERVAL 20
1005
1006 bool SOSAccountSyncWithIDSPeer(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error)
1007 {
1008 CFErrorRef localError = NULL;
1009 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1010 CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1011 static int engineLogCountDown = 0;
1012 bool result = true;
1013
1014 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
1015 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
1016 NULL, &localError));
1017 SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
1018 CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
1019 if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
1020 CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
1021 }
1022 CFReleaseNull(peerDeviceID);
1023 });
1024
1025 require_action_quiet(CFArrayGetCount(ids), xit, SOSCreateError(kSOSErrorNoCircle, CFSTR("Cannot find peer in circle"),
1026 NULL, &localError));
1027
1028 secnotice("IDS Transport","Syncing with IDS capable peer: %@", ids);
1029 CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(account->trusted_circle), ids);
1030 result = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToPeerIDs, &localError);
1031 secnotice("IDS Transport", "IDS Sync result: %d", result);
1032
1033 SOSEngineRef engine = SOSTransportMessageGetEngine(account->ids_message_transport);
1034 result &= SOSEngineSyncWithPeers(engine, &localError);
1035
1036 if (result)
1037 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
1038
1039 if(engineLogCountDown <= 0) {
1040 SOSEngineLogState(engine);
1041 engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
1042 } else {
1043 engineLogCountDown--;
1044 }
1045
1046 xit:
1047 CFReleaseNull(circleToPeerIDs);
1048
1049 if (!result) {
1050 secdebug("Account", "Could not sync with peer %@, error: %@", deviceID, localError);
1051 // Tell account to update SOSEngine with current trusted peers
1052 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
1053 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
1054 account->engine_peer_state_needs_repair = true;
1055 }
1056 CFErrorPropagate(localError, error);
1057 localError = NULL;
1058 }
1059 CFReleaseNull(ids);
1060 CFReleaseSafe(localError);
1061
1062 return result;
1063 }
1064
1065
1066
1067 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
1068 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
1069 {
1070 bool success = true;
1071
1072 SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1073 require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
1074 require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
1075
1076 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
1077
1078 CFStringRef circle_name = SOSCircleGetName(circle);
1079
1080 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1081 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
1082
1083 CFErrorRef localError = NULL;
1084 if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
1085 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
1086 }
1087
1088 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
1089 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
1090 }
1091
1092 CFReleaseNull(localError);
1093
1094 if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
1095 if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
1096 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
1097 }
1098 }
1099 CFReleaseNull(localError);
1100 CFReleaseNull(circleToPeerIDs);
1101
1102 xit:
1103 return success;
1104 }
1105
1106 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
1107 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
1108
1109 __block bool success = true;
1110
1111 CFSetForEach(account->retirees, ^(const void *value) {
1112 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
1113
1114 if (retiree) {
1115 // Remove the entry if it's not a retired peer or if it's retirment ticket has expired AND he's no longer in the circle.
1116 if (!SOSPeerInfoIsRetirementTicket(retiree) ||
1117 (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
1118 CFSetAddValue(retirees_to_remove, retiree);
1119 };
1120 }
1121 });
1122
1123 CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1124 CFSetForEach(retirees_to_remove, ^(const void *value) {
1125 CFArrayAppendValue(retirees_to_cleanup, value);
1126 CFSetRemoveValue(account->retirees, value);
1127 });
1128
1129 CFReleaseNull(retirees_to_remove);
1130
1131 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1132 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
1133 NULL);
1134
1135 CFReleaseNull(retirees_to_cleanup);
1136
1137 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
1138
1139 CFReleaseNull(retirements_to_remove);
1140
1141 return success;
1142 }
1143
1144 bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
1145 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
1146 CFSetSetValue(account->retirees, peer);
1147 CFErrorRef cleanupError = NULL;
1148 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
1149 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
1150 }
1151 CFReleaseSafe(cleanupError);
1152 });
1153 return true;
1154 }
1155
1156 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
1157 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
1158 SOSFullPeerInfoRef meFull = SOSAccountGetMyFullPeerInfo(account);
1159 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(meFull);
1160 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
1161
1162 if(!new_circle) return NULL;
1163 __block bool workDone = false;
1164 if (account->retirees) {
1165 CFSetForEach(account->retirees, ^(const void* value) {
1166 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
1167 if (isSOSPeerInfo(pi)) {
1168 SOSCircleUpdatePeerInfo(new_circle, pi);
1169 workDone = true;
1170 }
1171 });
1172 }
1173
1174 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
1175 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
1176
1177 if(iAmApplicant) {
1178 if(userPrivKey) {
1179 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
1180 if(!SOSCircleResetToOffering(new_circle, userPrivKey, meFull, error) ||
1181 !SOSAccountAddiCloudIdentity(account, new_circle, userPrivKey, error)) {
1182 CFReleaseNull(new_circle);
1183 return NULL;
1184 }
1185 } else {
1186 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
1187 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
1188 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
1189 // handleUpdateCircle to return false.
1190 CFReleaseNull(new_circle);
1191 return NULL;
1192 }
1193 } else {
1194 // This case is when we aren't an applicant and the circle is retirement-empty.
1195 secnotice("resetToEmpty", "Reset to empty with last retirement");
1196 SOSCircleResetToEmpty(new_circle, NULL);
1197 }
1198 }
1199
1200 return new_circle;
1201 }
1202
1203 //
1204 // MARK: Circle Membership change notificaion
1205 //
1206
1207 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1208 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
1209 CFArrayAppendValue(a->change_blocks, copy);
1210 CFReleaseNull(copy);
1211 }
1212
1213 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
1214 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
1215 }
1216
1217 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
1218 if (!changeBlock) return;
1219
1220 CFRetainSafe(ds_name);
1221 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
1222 CFSetRef added_peers, CFSetRef removed_peers,
1223 CFSetRef added_applicants, CFSetRef removed_applicants) {
1224
1225 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
1226 return;
1227
1228 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
1229 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
1230
1231 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1232 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1233 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1234
1235 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
1236 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
1237 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
1238 });
1239
1240 CFSetForEach(added_peers, ^(const void *value) {
1241 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
1242 });
1243
1244 CFSetForEach(removed_peers, ^(const void *value) {
1245 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
1246 });
1247 }
1248
1249 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
1250 changeBlock(peer_ids, added_ids, removed_ids);
1251
1252 CFReleaseSafe(peer_ids);
1253 CFReleaseSafe(added_ids);
1254 CFReleaseSafe(removed_ids);
1255 };
1256
1257 CFRetainSafe(changeBlock);
1258 SOSAccountAddChangeBlock(a, block_to_register);
1259
1260 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
1261 if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
1262 block_to_register(a->trusted_circle, empty, empty, empty, empty);
1263 }
1264 CFReleaseSafe(empty);
1265 }
1266
1267 void SOSAccountPurgeIdentity(SOSAccountRef account) {
1268 if (account->my_identity) {
1269 // Purge private key but don't return error if we can't.
1270 CFErrorRef purgeError = NULL;
1271 if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
1272 secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
1273 }
1274 CFReleaseNull(purgeError);
1275
1276 CFReleaseNull(account->my_identity);
1277 }
1278 }
1279
1280 bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1281 SOSFullPeerInfoRef fpi = account->my_identity;
1282 if(!fpi) return false;
1283
1284 CFErrorRef localError = NULL;
1285
1286 bool retval = false;
1287
1288 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1289 if (!retire_peer) {
1290 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1291 } else {
1292 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1293 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1294 // Remove our application if we have one.
1295 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1296 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1297 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1298 CFErrorRef cleanupError = NULL;
1299 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
1300 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1301 }
1302 CFReleaseSafe(cleanupError);
1303 }
1304 }
1305
1306 // Store the retirement record locally.
1307 CFSetAddValue(account->retirees, retire_peer);
1308
1309 // Write retirement to Transport
1310 CFErrorRef postError = NULL;
1311 if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
1312 secwarning("Couldn't post retirement (%@)", postError);
1313 }
1314 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
1315 secwarning("Couldn't flush retirement data (%@)", postError);
1316 }
1317 CFReleaseNull(postError);
1318 }
1319
1320 SOSAccountPurgeIdentity(account);
1321
1322 retval = true;
1323
1324 CFReleaseNull(localError);
1325 CFReleaseNull(retire_peer);
1326 return retval;
1327 }
1328
1329 bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
1330 SOSFullPeerInfoRef fpi = account->my_identity;
1331 if(!fpi) return false;
1332 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1333 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1334
1335 CFErrorRef localError = NULL;
1336
1337 bool retval = false;
1338 bool writeRing = false;
1339 bool writePeerInfo = false;
1340
1341 if(SOSRingHasPeerID(ring, peerID)) {
1342 writePeerInfo = true;
1343 }
1344
1345 #if 0
1346 // this was circle behavior - at some point
1347 if(SOSRingHasApplicant(ring, peerID)) {
1348 writeRing = true;
1349 }
1350 #endif
1351
1352 if(writePeerInfo || writeRing) {
1353 SOSRingWithdraw(ring, NULL, fpi, error);
1354 }
1355
1356 // Write leave thing to Transport
1357 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
1358 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
1359
1360 if (writeRing) {
1361 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1362
1363 if (ring_data) {
1364 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
1365 }
1366 CFReleaseNull(ring_data);
1367 }
1368 retval = true;
1369 CFReleaseNull(localError);
1370 return retval;
1371 }
1372
1373 bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
1374 bool result = false;
1375 SOSTransportCircleRef transport = account->circle_transport;
1376 if (transport) {
1377 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
1378 }
1379 return result;
1380 }
1381
1382 /*
1383 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1384 local value that has been overwritten by a distant value. If there is no
1385 conflict between the local and the distant values when doing the initial
1386 sync (e.g. if the cloud has no data stored or the client has not stored
1387 any data yet), you'll never see that notification.
1388
1389 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1390 with server but initial round trip with server does not imply
1391 NSUbiquitousKeyValueStoreInitialSyncChange.
1392 */
1393
1394
1395 //
1396 // MARK: Status summary
1397 //
1398
1399 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
1400 if (!circle)
1401 return kSOSCCNotInCircle;
1402
1403 if (circle && SOSCircleCountPeers(circle) == 0)
1404 return kSOSCCCircleAbsent;
1405
1406 if (this_peer) {
1407
1408 if(SOSPeerInfoIsRetirementTicket(this_peer))
1409 return kSOSCCNotInCircle;
1410
1411 if (SOSCircleHasPeer(circle, this_peer, NULL))
1412 return kSOSCCInCircle;
1413
1414 if (SOSCircleHasApplicant(circle, this_peer, NULL))
1415 return kSOSCCRequestPending;
1416 }
1417
1418 return kSOSCCNotInCircle;
1419 }
1420
1421 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1422 switch(status) {
1423 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1424 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1425 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1426 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1427 case kSOSCCError: return CFSTR("kSOSCCError");
1428 }
1429 return CFSTR("kSOSCCError");
1430 }
1431
1432 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
1433 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
1434
1435 if (result != kSOSCCInCircle) {
1436 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
1437 return false;
1438 }
1439
1440 return true;
1441 }
1442
1443 SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
1444 if (!SOSAccountHasPublicKey(account, error)) {
1445 return kSOSCCError;
1446 }
1447
1448 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
1449 }
1450
1451 //
1452 // MARK: Account Reset Circles
1453 //
1454
1455 // This needs to be called within a SOSAccountModifyCircle() block
1456
1457 bool SOSAccountAddiCloudIdentity(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
1458 bool result = false;
1459 SOSFullPeerInfoRef cloud_identity = NULL;
1460 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
1461 require_quiet(cloud_peer, err_out);
1462 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
1463 CFReleaseNull(cloud_peer);
1464 require_quiet(cloud_identity, err_out);
1465 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, error), err_out);
1466 require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), error), err_out);
1467 result = true;
1468 err_out:
1469 return result;
1470 }
1471
1472 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1473 bool retval = false;
1474 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1475
1476 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1477 if(SOSPeerInfoIsCloudIdentity(peer)) {
1478 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1479 if(!icfpi) {
1480 CFSetAddValue(iCloud2Remove, peer);
1481 }
1482 CFReleaseNull(icfpi);
1483 }
1484 });
1485
1486 if(CFSetGetCount(iCloud2Remove) > 0) {
1487 retval = true;
1488 SOSCircleRemovePeers(circle, privKey, account->my_identity, iCloud2Remove, error);
1489 }
1490 CFReleaseNull(iCloud2Remove);
1491 return retval;
1492 }
1493
1494 static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn, SecKeyRef user_key, CFErrorRef *error) {
1495 SOSAccountRef account = aTxn->account;
1496 bool result = false;
1497
1498 require(SOSAccountHasCircle(account, error), fail);
1499 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1500
1501 (void) SOSAccountResetAllRings(account, error);
1502
1503 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1504 bool result = false;
1505 SOSFullPeerInfoRef cloud_identity = NULL;
1506 CFErrorRef localError = NULL;
1507
1508 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1509
1510 account->departure_code = kSOSNeverLeftCircle;
1511 require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
1512
1513 require_quiet(SOSAccountAddiCloudIdentity(account, circle, user_key, error), err_out);
1514 result = true;
1515 SOSAccountPublishCloudParameters(account, NULL);
1516
1517 err_out:
1518 if (result == false)
1519 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1520 if (localError && error && *error == NULL) {
1521 *error = localError;
1522 localError = NULL;
1523 }
1524 CFReleaseNull(localError);
1525 CFReleaseNull(cloud_identity);
1526 return result;
1527 });
1528
1529 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1530 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1531
1532 result = true;
1533
1534 fail:
1535 return result;
1536 }
1537
1538
1539 bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1540 SOSAccountRef account = aTxn->account;
1541 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1542 if (!user_key)
1543 return false;
1544
1545 CFReleaseNull(account->my_identity);
1546 secnotice("resetToOffering", "Resetting circle to offering by request from client");
1547
1548 return user_key && SOSAccountResetCircleToOffering(aTxn, user_key, error);
1549 }
1550
1551 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1552 if (!SOSAccountHasPublicKey(account, error))
1553 return false;
1554 __block bool result = true;
1555
1556 result &= SOSAccountResetAllRings(account, error);
1557
1558 CFReleaseNull(account->my_identity);
1559
1560 account->departure_code = kSOSWithdrewMembership;
1561 secnotice("resetToEmpty", "Reset Circle to empty by client request");
1562 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1563 result = SOSCircleResetToEmpty(circle, error);
1564 return result;
1565 });
1566
1567 if (!result) {
1568 secerror("error: %@", error ? *error : NULL);
1569 }
1570 return result;
1571 }
1572 //
1573 // MARK: start backups
1574 //
1575
1576 bool SOSAccountEnsureInBackupRings(SOSAccountRef account) {
1577 __block bool result = false;
1578 __block CFErrorRef error = NULL;
1579 secnotice("backup", "Ensuring in rings");
1580
1581 CFDataRef backupKey = NULL;
1582
1583 require_action_quiet(account->backup_key, exit, result = true);
1584
1585 backupKey = SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey);
1586
1587 require_action_quiet(!CFEqualSafe(backupKey, account->backup_key), exit, result = true); // If we're already set up, we're done.
1588 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1589 return SOSFullPeerInfoUpdateBackupKey(fpi, account->backup_key, error);
1590 }), exit);
1591
1592 require_quiet(account->backup_key, exit); // If it went null, we're done now.
1593
1594 require_quiet(SOSBSKBIsGoodBackupPublic(account->backup_key, &error), exit);
1595
1596 // It's a good key, we're going with it. Stop backing up the old way.
1597 CFErrorRef localError = NULL;
1598 if (!SOSDeleteV0Keybag(&localError)) {
1599 secerror("Failed to delete v0 keybag: %@", localError);
1600 }
1601 CFReleaseNull(localError);
1602
1603 result = true;
1604
1605 // Setup backups the new way.
1606 SOSAccountForEachBackupView(account, ^(const void *value) {
1607 CFStringRef viewName = (CFStringRef)value;
1608 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1609 });
1610
1611 exit:
1612 if (!result) {
1613 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1614 }
1615 CFReleaseNull(backupKey);
1616 return result;
1617 }
1618
1619 //
1620 // MARK: Joining
1621 //
1622
1623 static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn, SecKeyRef user_key,
1624 bool use_cloud_peer, CFErrorRef* error) {
1625 SOSAccountRef account = aTxn->account;
1626
1627 __block bool result = false;
1628 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1629
1630 require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1631 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1632
1633 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1634
1635 if (SOSCircleCountPeers(account->trusted_circle) == 0) {
1636 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1637 // this also clears initial sync data
1638 result = SOSAccountResetCircleToOffering(aTxn, user_key, error);
1639 } else {
1640 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1641
1642 if (use_cloud_peer) {
1643 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1644 }
1645
1646 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1647 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1648 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1649 account->departure_code = kSOSNeverLeftCircle;
1650 if(result && cloud_full_peer) {
1651 CFErrorRef localError = NULL;
1652 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1653 require_quiet(cloudid, finish);
1654 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1655 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1656
1657 finish:
1658 if (localError){
1659 secerror("Failed to join with cloud identity: %@", localError);
1660 CFReleaseNull(localError);
1661 }
1662 }
1663 return result;
1664 });
1665
1666 if (use_cloud_peer) {
1667 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1668 }
1669 }
1670
1671 fail:
1672 CFReleaseNull(cloud_full_peer);
1673 return result;
1674 }
1675
1676 static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn, bool use_cloud_identity, CFErrorRef* error) {
1677 SOSAccountRef account = aTxn->account;
1678 bool success = false;
1679
1680 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1681 require_quiet(user_key, done); // Fail if we don't get one.
1682
1683 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1684
1685 if (account->my_identity != NULL) {
1686 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1687 success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
1688 require_quiet(!success, done);
1689
1690 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1691
1692 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1693 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1694
1695 CFReleaseNull(account->my_identity);
1696 myPeer = NULL;
1697 }
1698 }
1699
1700 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1701
1702 require_quiet(success, done);
1703
1704 account->departure_code = kSOSNeverLeftCircle;
1705
1706 done:
1707 return success;
1708 }
1709
1710 bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1711 return SOSAccountJoinCircles_internal(aTxn, false, error);
1712 }
1713
1714 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1715 CFStringRef result = NULL;
1716
1717 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1718
1719 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1720
1721 fail:
1722 return result;
1723 }
1724
1725 bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
1726 bool result = true;
1727
1728 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1729 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1730 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1731
1732 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1733
1734 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1735 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDSV2, error);
1736 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanFalse, error);
1737 SOSFullPeerInfoUpdateTransportFragmentationPreference(account->my_identity, kCFBooleanTrue, error);
1738
1739 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1740 });
1741 }
1742 else
1743 result = false;
1744
1745 SOSCCSyncWithAllPeers();
1746
1747 fail:
1748 CFReleaseNull(account->deviceID);
1749 account->deviceID = CFRetainSafe(IDS);
1750 return result;
1751 }
1752
1753 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1754 bool result = true;
1755 //construct message dictionary, circle -> peerID -> message
1756
1757 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1758 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1759
1760 char *messageCharStar;
1761 asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
1762 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1763
1764 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, messageString, kIDSMessageToSendKey, CFSTR("send IDS test message"), NULL);
1765
1766 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1767 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
1768 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1769 });
1770
1771 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1772 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1773
1774 CFReleaseNull(mutableDictionary);
1775 CFReleaseNull(peerToMessage);
1776 CFReleaseNull(circleToPeerMessages);
1777 CFReleaseNull(messageString);
1778 free(messageCharStar);
1779 return result;
1780 }
1781
1782 bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1783 bool result = false;
1784 //construct message dictionary, circle -> peerID -> message
1785
1786 if(account->ids_message_transport == NULL)
1787 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1788
1789 require_quiet(account->ids_message_transport, fail);
1790 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1791 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1792
1793 char *messageCharStar;
1794 asprintf(&messageCharStar, "%d", kIDSStartPingTestMessage);
1795 CFStringRef operationToString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1796 CFStringRef messageToSend = CFSTR("send IDS test message");
1797
1798 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationToString, kIDSMessageToSendKey, messageToSend, NULL);
1799
1800
1801 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1802 if(CFStringCompare(SOSAccountGetMyPeerID(account), SOSPeerInfoGetPeerID(peer), 0) != 0)
1803 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1804 });
1805
1806 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1807 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1808
1809 CFReleaseNull(mutableDictionary);
1810 CFReleaseNull(peerToMessage);
1811 CFReleaseNull(circleToPeerMessages);
1812 CFReleaseNull(operationToString);
1813 free(messageCharStar);
1814 fail:
1815 return result;
1816 }
1817
1818 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
1819 bool result = true;
1820
1821 __block bool success = true;
1822 __block CFErrorRef localError = NULL;
1823 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1824 dispatch_retain(wait_for); // Both this scope and the block own it
1825
1826 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1827 success = (sync_error == NULL);
1828 if (!success) {
1829 CFRetainAssign(localError, sync_error);
1830 }
1831
1832 dispatch_semaphore_signal(wait_for);
1833 dispatch_release(wait_for);
1834 });
1835
1836 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1837 dispatch_release(wait_for);
1838
1839 if(!success && localError != NULL && error != NULL){
1840 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
1841 *error = localError;
1842 result = false;
1843 }
1844 else{
1845 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1846 }
1847 return result;
1848 }
1849
1850 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1851 return SOSAccountJoinCircles_internal(aTxn, true, error);
1852 }
1853
1854
1855 bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1856 {
1857 bool result = true;
1858
1859 secnotice("leaveCircle", "Leaving circle by client request");
1860 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1861 return sosAccountLeaveCircle(account, circle, error);
1862 });
1863
1864 account->departure_code = kSOSWithdrewMembership;
1865
1866 return result;
1867 }
1868
1869 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CFErrorRef* error)
1870 {
1871 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1872 if (!user_key)
1873 return false;
1874
1875 bool result = true;
1876
1877 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1878
1879 bool leaveCircle = CFSetContainsValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1880
1881 CFSetRemoveValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1882
1883 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1884 bool success = false;
1885
1886 if(CFSetGetCount(peersToRemove) != 0) {
1887 require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
1888 success = SOSAccountGenerationSignatureUpdate(account, error);
1889 } else success = true;
1890
1891 if (success && leaveCircle) {
1892 secnotice("leaveCircle", "Leaving circle by client request");
1893 success = sosAccountLeaveCircle(account, circle, error);
1894 }
1895
1896 done:
1897 return success;
1898
1899 });
1900
1901 return result;
1902 }
1903
1904
1905 bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
1906 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1907 dispatch_group_t group = dispatch_group_create();
1908 __block bool result = false;
1909 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1910 // Add a task to the group
1911 dispatch_group_async(group, queue, ^{
1912 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1913 secnotice("leaveCircle", "Leaving circle by client request");
1914 return sosAccountLeaveCircle(account, circle, error);
1915 });
1916 });
1917 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1918 dispatch_group_wait(group, milestone);
1919
1920 account->departure_code = kSOSWithdrewMembership;
1921
1922 dispatch_release(group);
1923 return result;
1924 }
1925
1926
1927 //
1928 // MARK: Application
1929 //
1930
1931 static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
1932 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1933 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1934 CFErrorRef peer_error = NULL;
1935 if (account->trusted_circle && me &&
1936 SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
1937 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
1938 __block bool modified = false;
1939 CFArrayForEach(peer_infos, ^(const void *value) {
1940 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1941 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1942 if (action(circle, account->my_identity, peer)) {
1943 modified = true;
1944 }
1945 }
1946 });
1947 return modified;
1948 });
1949 }
1950 if (peer_error)
1951 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1952 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1953 }
1954
1955 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1956 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1957 if (!user_key)
1958 return false;
1959
1960 __block bool success = true;
1961 __block int64_t num_peers = 0;
1962
1963 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1964 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1965 if (!accepted)
1966 success = false;
1967 else
1968 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1969 return accepted;
1970 });
1971
1972 return success;
1973 }
1974
1975 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1976 __block bool success = true;
1977 __block int64_t num_peers = 0;
1978
1979 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1980 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1981 if (!rejected)
1982 success = false;
1983 else
1984 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1985 return rejected;
1986 });
1987
1988 return success;
1989 }
1990
1991
1992 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1993 return CFSTR("We're compatible, go away");
1994 }
1995
1996 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1997 return account->departure_code;
1998 }
1999
2000 void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
2001 account->departure_code = reason;
2002 }
2003
2004
2005 CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
2006 CFArrayRef result = NULL;
2007 CFNumberRef generation = NULL;
2008
2009 require_quiet(SOSAccountHasPublicKey(account, error), fail);
2010 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
2011
2012 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
2013 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
2014
2015 fail:
2016 return result;
2017 }
2018
2019 bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
2020 if (!SOSAccountHasPublicKey(account, error))
2021 return NULL;
2022
2023 return account->user_public_trusted;
2024 }
2025
2026 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
2027 // TODO: this result is never set or used
2028 bool result = true;
2029
2030 secnotice("updates", "Ensuring peer registration.");
2031
2032 require_quiet(account->trusted_circle, done);
2033 require_quiet(account->my_identity, done);
2034 require_quiet(account->user_public_trusted, done);
2035
2036 // If we are not in the circle, there is no point in setting up peers
2037 require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
2038
2039 // This code only uses the SOSFullPeerInfoRef for two things:
2040 // - Finding out if this device is in the trusted circle
2041 // - Using the peerID for this device to see if the current peer is "me"
2042 // - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder
2043
2044 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
2045
2046 SOSCircleForEachValidSyncingPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
2047 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
2048 CFErrorRef localError = NULL;
2049 SOSTransportMessageRef messageTransport = NULL;
2050
2051 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
2052
2053 SOSEngineInitializePeerCoder(messageTransport->engine, account->my_identity, peer, &localError);
2054 if (localError)
2055 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
2056 CFReleaseSafe(localError);
2057 }
2058 });
2059
2060 //Initialize our device ID
2061 SOSTransportMessageIDSGetIDSDeviceID(account);
2062
2063
2064 done:
2065 return result;
2066 }
2067
2068 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
2069 if (!account->expansion) {
2070 account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
2071 }
2072
2073 return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
2074 }
2075
2076 //
2077 // Value manipulation
2078 //
2079
2080 bool SOSAccountClearValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
2081 bool success = SOSAccountEnsureExpansion(account, error);
2082 require_quiet(success, errOut);
2083
2084 CFDictionaryRemoveValue(account->expansion, key);
2085 errOut:
2086 return success;
2087 }
2088
2089 bool SOSAccountSetValue(SOSAccountRef account, CFStringRef key, CFTypeRef value, CFErrorRef *error) {
2090 if (value == NULL) return SOSAccountClearValue(account, key, error);
2091
2092 bool success = SOSAccountEnsureExpansion(account, error);
2093 require_quiet(success, errOut);
2094
2095 CFDictionarySetValue(account->expansion, key, value);
2096 errOut:
2097 return success;
2098 }
2099
2100 CFTypeRef SOSAccountGetValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
2101 if (!account->expansion) {
2102 return NULL;
2103 }
2104 return CFDictionaryGetValue(account->expansion, key);
2105 }
2106
2107 bool SOSAccountAddEscrowRecords(SOSAccountRef account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
2108 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
2109 CFMutableDictionaryRef escrowCopied = NULL;
2110 bool success = false;
2111
2112 if(isDictionary(escrowRecords) && escrowRecords != NULL)
2113 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
2114 else
2115 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2116
2117 CFDictionaryAddValue(escrowCopied, dsid, record);
2118 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
2119
2120 if(*error == NULL)
2121 success = true;
2122
2123 CFReleaseNull(escrowCopied);
2124
2125 return success;
2126
2127 }
2128
2129 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
2130 bool success = false;
2131
2132 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
2133 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
2134
2135 return success;
2136 }
2137
2138 bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
2139 {
2140 CFMutableDictionaryRef circleToPeerMessages = NULL;
2141 CFStringRef operationTypeAsString = NULL;
2142 CFMutableDictionaryRef mutableDictionary = NULL;
2143 CFMutableSetRef peers = NULL;
2144 CFMutableDictionaryRef peerList = NULL;
2145 char* message = NULL;
2146 bool result = false;
2147 if(account->ids_message_transport == NULL)
2148 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
2149
2150 require_quiet(account->ids_message_transport, fail);
2151 circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2152
2153 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
2154
2155 asprintf(&message, "%d", kIDSPeerAvailability);
2156 operationTypeAsString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
2157
2158 mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationTypeAsString, kIDSMessageToSendKey, CFSTR("checking peers"), NULL);
2159
2160 //make sure there are peers in the circle
2161 peers = SOSCircleCopyPeers(account->trusted_circle, kCFAllocatorDefault);
2162 require_quiet(CFSetGetCount(peers) > 0, fail);
2163 CFReleaseNull(peers);
2164
2165 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2166 SOSCircleRef circle = account->trusted_circle;
2167
2168 //check each peer to make sure they have the right view set enabled
2169 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
2170 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
2171 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account))){
2172 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
2173 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
2174 if(CFEqualSafe(intersectSets, mySubSet)){
2175 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
2176 if(deviceID != NULL)
2177 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), mutableDictionary);
2178 CFReleaseNull(deviceID);
2179 }
2180 CFReleaseNull(peerViews);
2181 CFReleaseNull(intersectSets);
2182 }
2183 });
2184
2185 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
2186 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerList);
2187 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
2188
2189 fail:
2190 CFReleaseNull(mutableDictionary);
2191 CFReleaseNull(operationTypeAsString);
2192 CFReleaseNull(peerList);
2193 CFReleaseNull(circleToPeerMessages);
2194 CFReleaseNull(peers);
2195 free(message);
2196 return result;
2197 }
2198
2199
2200 void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
2201 if (!SOSAccountIsInCircle(account, NULL))
2202 return;
2203
2204 SOSAccountModifyCircle(account, NULL, ^bool (SOSCircleRef circle) {
2205 __block bool updated = false;
2206 CFSetForEach(account->retirees, ^(CFTypeRef element){
2207 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
2208
2209 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
2210 updated = true;
2211 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
2212 CFErrorRef cleanupError = NULL;
2213 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
2214 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
2215 CFReleaseSafe(cleanupError);
2216 }
2217 });
2218 return updated;
2219 });
2220 }
2221
2222
2223 static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error) {
2224 size_t total_payload = 0;
2225
2226 CFDataRef publicBytes = NULL;
2227 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
2228
2229 if (result != errSecSuccess) {
2230 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
2231 return 0;
2232 }
2233
2234 require_quiet(accumulate_size(&total_payload, der_sizeof_number(gencount, error)), errOut);
2235 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(publicBytes, error)), errOut);
2236 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(signature, error)), errOut);
2237 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
2238
2239 errOut:
2240 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
2241 return 0;
2242 }
2243
2244 static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
2245 CFDataRef publicBytes = NULL;
2246
2247 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
2248
2249 if (result != errSecSuccess) {
2250 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
2251 return NULL;
2252 }
2253
2254
2255 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2256 der_encode_number(gencount, error, der,
2257 der_encode_data_or_null(publicBytes, error, der,
2258 der_encode_data_or_null(signature, error, der, der_end))));
2259 return der_end;
2260 }
2261
2262 static CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFAllocatorRef allocator, CFErrorRef *error)
2263 {
2264 return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
2265 return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
2266 });
2267 }
2268
2269 struct piggyBackBlob {
2270 SOSGenCountRef gencount;
2271 SecKeyRef pubKey;
2272 CFDataRef signature;
2273 };
2274
2275 static struct piggyBackBlob *SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator, CFErrorRef *error,
2276 const uint8_t** der_p, const uint8_t *der_end) {
2277 const uint8_t *sequence_end;
2278 struct piggyBackBlob *retval = NULL;
2279 SOSGenCountRef gencount = NULL;
2280 CFDataRef signature = NULL;
2281 CFDataRef publicBytes = NULL;
2282
2283 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
2284 require_action_quiet(sequence_end != NULL, errOut,
2285 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
2286 *der_p = der_decode_number(allocator, 0, &gencount, error, *der_p, sequence_end);
2287 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, der_end);
2288 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, der_end);
2289 require_action_quiet(*der_p && *der_p == der_end, errOut,
2290 SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
2291 retval = malloc(sizeof(struct piggyBackBlob));
2292 retval->gencount = gencount;
2293 retval->signature = signature;
2294 retval->pubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
2295
2296 errOut:
2297 if(!retval) {
2298 CFReleaseNull(gencount);
2299 CFReleaseNull(publicBytes);
2300 CFReleaseNull(signature);
2301 }
2302 return retval;
2303 }
2304
2305 static struct piggyBackBlob *SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator, CFDataRef blobData, CFErrorRef *error)
2306 {
2307 size_t size = CFDataGetLength(blobData);
2308 const uint8_t *der = CFDataGetBytePtr(blobData);
2309 struct piggyBackBlob *inflated = SOSPiggyBackBlobCreateFromDER(allocator, error, &der, der + size);
2310 return inflated;
2311 }
2312
2313
2314
2315 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccountRef account, CFErrorRef* error) {
2316 SOSPeerInfoRef applicant = NULL;
2317 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2318 if(!userKey) return false;
2319 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), errOut);
2320 require(SOSFullPeerInfoPromoteToApplication(account->my_identity, userKey, error), errOut);
2321 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, (SOSFullPeerInfoGetPeerInfo(account->my_identity)), error);
2322 errOut:
2323 return applicant;
2324 }
2325
2326
2327 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccountRef account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2328 SOSGenCountRef gencount = NULL;
2329 CFDataRef signature = NULL;
2330 SecKeyRef ourKey = NULL;
2331
2332 CFDataRef pbblob = NULL;
2333
2334 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2335 require_quiet(userKey, errOut);
2336
2337 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2338 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2339
2340 {
2341 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
2342 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2343 require_quiet(ourKey, errOut);
2344 }
2345
2346 SOSCircleRef currentCircle = SOSAccountGetCircle(account, error);
2347 require_quiet(currentCircle, errOut);
2348
2349 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2350 require_quiet(prunedCircle, errOut);
2351 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2352
2353 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2354
2355 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2356 require_quiet(signature, errOut);
2357
2358 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, kCFAllocatorDefault, error);
2359
2360 errOut:
2361 CFReleaseNull(gencount);
2362 CFReleaseNull(signature);
2363 CFReleaseNull(ourKey);
2364
2365 return pbblob;
2366 }
2367
2368 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account, CFDataRef joiningBlob, CFErrorRef *error) {
2369 bool retval = false;
2370 SecKeyRef userKey = NULL;
2371 struct piggyBackBlob *pbb = NULL;
2372
2373 userKey = SOSAccountGetPrivateCredential(account, error);
2374 require_quiet(userKey, errOut);
2375 pbb = SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault, joiningBlob, error);
2376 require_quiet(pbb, errOut);
2377
2378 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2379
2380 retval = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef copyOfCurrent) {
2381 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2382 pbb->gencount,
2383 pbb->pubKey,
2384 pbb->signature,
2385 account->my_identity, error);;
2386
2387 });
2388
2389 errOut:
2390 if(pbb) {
2391 CFReleaseNull(pbb->gencount);
2392 CFReleaseNull(pbb->pubKey);
2393 CFReleaseNull(pbb->signature);
2394 free(pbb);
2395 }
2396 return retval;
2397 }
2398
2399 static char boolToChars(bool val, char truechar, char falsechar) {
2400 return val? truechar: falsechar;
2401 }
2402
2403 #define ACCOUNTLOGSTATE "accountLogState"
2404 void SOSAccountLogState(SOSAccountRef account) {
2405 bool hasPubKey = account->user_public != NULL;
2406 bool pubTrusted = account->user_public_trusted;
2407 bool hasPriv = account->_user_private != NULL;
2408 SOSCCStatus stat = SOSAccountGetCircleStatus(account, NULL);
2409 CFStringRef userPubKeyID = (account->user_public) ? SOSCopyIDOfKeyWithLength(account->user_public, 8, NULL):
2410 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2411
2412 secnotice(ACCOUNTLOGSTATE, "Start");
2413
2414 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2415 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2416 userPubKeyID,
2417 SOSAccountGetSOSCCStatusString(stat)
2418 );
2419 CFReleaseNull(userPubKeyID);
2420 if(account->trusted_circle) SOSCircleLogState(ACCOUNTLOGSTATE, account->trusted_circle, account->user_public, SOSAccountGetMyPeerID(account));
2421 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2422 }
2423
2424 void SOSAccountLogViewState(SOSAccountRef account) {
2425 bool isInCircle = SOSAccountIsInCircle(account, NULL);
2426 require_quiet(isInCircle, imOut);
2427 SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
2428 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2429 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2430
2431 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2432 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2433 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2434 boolToChars(isInitialComplete, 'I', 'i'),
2435 boolToChars(isBackupComplete, 'B', 'b'),
2436 description);
2437 });
2438 CFReleaseNull(views);
2439 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2440 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2441 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2442 });
2443 CFReleaseNull(unsyncedViews);
2444
2445 imOut:
2446 secnotice(ACCOUNTLOGSTATE, "Finish");
2447
2448 return;
2449 }