2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
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/SecItemInternal.h>
27 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
28 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
30 CFGiblisWithCompareFor(SOSAccount
);
32 const CFStringRef SOSTransportMessageTypeIDS
= CFSTR("IDS");
33 const CFStringRef SOSTransportMessageTypeKVS
= CFSTR("KVS");
34 const CFStringRef kSOSDSIDKey
= CFSTR("AccountDSID");
35 const CFStringRef kSOSEscrowRecord
= CFSTR("EscrowRecord");
36 const CFStringRef kSOSUnsyncedViewsKey
= CFSTR("unsynced");
38 #define DATE_LENGTH 25
39 const CFStringRef kSOSAccountDebugScope
= CFSTR("Scope");
42 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
)
45 CFStringRef circle_name
= NULL
;
47 require_quiet(a
, xit
);
48 require_quiet(a
->factory
, xit
);
50 circle_name
= SOSDataSourceFactoryCopyName(a
->factory
);
51 require(circle_name
, xit
);
53 SOSAccountEnsureCircle(a
, circle_name
, NULL
);
58 // We don't own name, so don't release it.
59 CFReleaseNull(circle_name
);
64 SOSAccountRef
SOSAccountCreateBasic(CFAllocatorRef allocator
,
65 CFDictionaryRef gestalt
,
66 SOSDataSourceFactoryRef factory
) {
67 SOSAccountRef a
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
);
69 a
->queue
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
);
71 a
->notification_cleanups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
73 a
->gestalt
= CFRetainSafe(gestalt
);
75 a
->trusted_circle
= NULL
;
76 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(allocator
);
77 a
->backups
= CFDictionaryCreateMutableForCFTypes(allocator
);
78 a
->my_identity
= NULL
;
79 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
81 a
->factory
= factory
; // We adopt the factory. kthanksbai.
83 a
->_user_private
= NULL
;
84 a
->_password_tmp
= NULL
;
85 a
->user_private_timer
= NULL
;
87 a
->change_blocks
= CFArrayCreateMutableForCFTypes(allocator
);
88 a
->waitForInitialSync_blocks
= CFDictionaryCreateMutableForCFTypes(allocator
);
89 a
->departure_code
= kSOSNeverAppliedToCircle
;
91 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
92 a
->circle_transport
= NULL
;
93 a
->kvs_message_transport
= NULL
;
94 a
->ids_message_transport
= NULL
;
95 a
->expansion
= CFDictionaryCreateMutableForCFTypes(allocator
);
100 SOSSecurityPropertyResultCode
SOSAccountUpdateSecurityProperty(SOSAccountRef account
, CFStringRef property
, SOSSecurityPropertyActionCode actionCode
, CFErrorRef
*error
) {
101 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralSecurityPropertyError
;
102 bool updateCircle
= false;
103 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
104 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
105 retval
= SOSFullPeerInfoUpdateSecurityProperty(account
->my_identity
, actionCode
, property
, error
);
107 if(actionCode
== kSOSCCSecurityPropertyEnable
&& retval
== kSOSCCSecurityPropertyValid
) {
109 } else if(actionCode
== kSOSCCSecurityPropertyDisable
&& retval
== kSOSCCSecurityPropertyNotValid
) {
111 } else if(actionCode
== kSOSCCSecurityPropertyPending
) {
116 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
117 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
118 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
126 SOSSecurityPropertyResultCode
SOSAccountSecurityPropertyStatus(SOSAccountRef account
, CFStringRef property
, CFErrorRef
*error
) {
127 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralViewError
;
128 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
129 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
130 retval
= SOSFullPeerInfoSecurityPropertyStatus(account
->my_identity
, property
, error
);
135 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
)
137 if (CFEqualSafe(new_gestalt
, account
->gestalt
))
140 if (account
->trusted_circle
&& account
->my_identity
141 && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) {
142 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
143 secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
144 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
148 CFRetainAssign(account
->gestalt
, new_gestalt
);
152 bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){
153 SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
);
154 //send new DSID over account changed
155 SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
);
160 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
, CFSetRef excludedViews
) {
161 if (account
->trusted_circle
&& account
->my_identity
) {
162 if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
, excludedViews
)) {
163 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
164 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
165 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
173 SOSViewResultCode
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
174 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
175 SOSViewResultCode currentStatus
= kSOSCCGeneralViewError
;
176 bool updateCircle
= false;
177 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
178 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
179 require_action_quiet((actionCode
== kSOSCCViewEnable
) || (actionCode
== kSOSCCViewDisable
), errOut
, CFSTR("Invalid View Action"));
180 currentStatus
= SOSAccountViewStatus(account
, viewname
, error
);
181 require_action_quiet((currentStatus
== kSOSCCViewNotMember
) || (currentStatus
== kSOSCCViewMember
), errOut
, CFSTR("View Membership Not Actionable"));
183 if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) {
184 // The V0 view switches on and off all on it's own, we allow people the delusion
185 // of control and status if it's what we're stuck at., otherwise error.
186 if (SOSAccountSyncingV0(account
)) {
187 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Can't disable V0 view and it's on right now"));
188 retval
= kSOSCCViewMember
;
190 require_action_quiet(actionCode
= kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now"));
191 retval
= kSOSCCViewNotMember
;
193 } else if (SOSAccountSyncingV0(account
) && SOSViewsIsV0Subview(viewname
)) {
194 // Subviews of V0 syncing can't be turned off if V0 is on.
195 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Have V0 peer can't disable"));
196 retval
= kSOSCCViewMember
;
198 if(actionCode
== kSOSCCViewEnable
&& currentStatus
== kSOSCCViewNotMember
) {
199 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
200 if(retval
== kSOSCCViewMember
) updateCircle
= true;
201 } else if(actionCode
== kSOSCCViewDisable
&& currentStatus
== kSOSCCViewMember
) {
202 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
203 if(retval
== kSOSCCViewNotMember
) updateCircle
= true;
205 retval
= currentStatus
;
209 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
210 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
211 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
220 SOSViewResultCode
SOSAccountViewStatus(SOSAccountRef account
, CFStringRef viewname
, CFErrorRef
*error
) {
221 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
222 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
223 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
225 retval
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
);
227 // 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
228 if (retval
!= kSOSCCViewMember
) {
229 if ((CFEqualSafe(viewname
, kSOSViewKeychainV0
) || SOSViewsIsV0Subview(viewname
))
230 && SOSAccountSyncingV0(account
)) {
231 retval
= kSOSCCViewMember
;
235 // If we're only an applicant we report pending if we would be a view member
236 if (retval
== kSOSCCViewMember
) {
237 bool isApplicant
= SOSCircleHasApplicant(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
), error
);
239 retval
= kSOSCCViewPending
;
247 static void dumpViewSet(CFStringRef label
, CFSetRef views
) {
249 secnotice("circleChange", "%@ list: %@", label
, views
);
251 secnotice("circleChange", "No %@ list provided.", label
);
255 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef enabledViews
, CFSetRef disabledViews
) {
256 bool updateCircle
= false;
257 dumpViewSet(CFSTR("Enabled"), enabledViews
);
258 dumpViewSet(CFSTR("Disabled"), disabledViews
);
260 require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle"));
261 require_action_quiet(account
->my_identity
, errOut
, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
262 require_action_quiet(enabledViews
|| disabledViews
, errOut
, secnotice("views", "No work to do"));
265 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
266 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
);
268 require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef"));
271 if(!SOSPeerInfoVersionIsCurrent(pi
)) {
272 if(!SOSPeerInfoUpdateToV2(pi
, NULL
)) {
273 secnotice("views", "Unable to update peer to V2- can't update views");
278 if(enabledViews
) updateCircle
= SOSViewSetEnable(pi
, enabledViews
);
279 if(disabledViews
) updateCircle
|= SOSViewSetDisable(pi
, disabledViews
);
281 /* UPDATE FULLPEERINFO VIEWS */
283 if (updateCircle
&& SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
)) {
284 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
285 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
286 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
295 SOSAccountRef
SOSAccountCreate(CFAllocatorRef allocator
,
296 CFDictionaryRef gestalt
,
297 SOSDataSourceFactoryRef factory
) {
298 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
300 SOSAccountEnsureFactoryCircles(a
);
302 SOSUpdateKeyInterest(a
);
307 static void SOSAccountDestroy(CFTypeRef aObj
) {
308 SOSAccountRef a
= (SOSAccountRef
) aObj
;
310 // We don't own the factory, merely have a reference to the singleton
314 SOSAccountCleanupNotificationForAllPeers(a
);
316 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
);
319 SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
);
321 dispatch_sync(a
->queue
, ^{
322 CFReleaseNull(a
->gestalt
);
324 CFReleaseNull(a
->my_identity
);
325 CFReleaseNull(a
->trusted_circle
);
326 CFReleaseNull(a
->trusted_rings
);
327 CFReleaseNull(a
->backups
);
328 CFReleaseNull(a
->retirees
);
330 a
->user_public_trusted
= false;
331 CFReleaseNull(a
->user_public
);
332 CFReleaseNull(a
->user_key_parameters
);
334 SOSAccountPurgePrivateCredential(a
);
335 CFReleaseNull(a
->previous_public
);
336 CFReleaseNull(a
->_user_private
);
337 CFReleaseNull(a
->_password_tmp
);
339 a
->departure_code
= kSOSNeverAppliedToCircle
;
340 CFReleaseNull(a
->kvs_message_transport
);
341 CFReleaseNull(a
->ids_message_transport
);
342 CFReleaseNull(a
->key_transport
);
343 CFReleaseNull(a
->circle_transport
);
344 dispatch_release(a
->queue
);
345 CFReleaseNull(a
->notification_cleanups
);
347 dispatch_release(a
->user_private_timer
);
348 CFReleaseNull(a
->change_blocks
);
349 CFReleaseNull(a
->waitForInitialSync_blocks
);
350 CFReleaseNull(a
->expansion
);
355 static OSStatus
do_delete(CFDictionaryRef query
) {
358 result
= SecItemDelete(query
);
360 secerror("SecItemDelete: %d", (int)result
);
366 do_keychain_delete_aks_bags()
369 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
370 kSecClass
, kSecClassGenericPassword
,
371 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
372 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
373 kSecAttrService
, CFSTR("SecureBackupService"),
374 kSecAttrSynchronizable
, kCFBooleanTrue
,
375 kSecUseTombstones
, kCFBooleanFalse
,
378 result
= do_delete(item
);
385 do_keychain_delete_identities()
388 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
389 kSecClass
, kSecClassKey
,
390 kSecAttrSynchronizable
, kCFBooleanTrue
,
391 kSecUseTombstones
, kCFBooleanFalse
,
392 kSecAttrAccessGroup
, CFSTR("com.apple.security.sos"),
395 result
= do_delete(item
);
402 do_keychain_delete_lakitu()
405 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
406 kSecClass
, kSecClassGenericPassword
,
407 kSecAttrSynchronizable
, kCFBooleanTrue
,
408 kSecUseTombstones
, kCFBooleanFalse
,
409 kSecAttrAccessGroup
, CFSTR("com.apple.lakitu"),
410 kSecAttrAccount
, CFSTR("EscrowServiceBypassToken"),
411 kSecAttrService
, CFSTR("EscrowService"),
414 result
= do_delete(item
);
421 do_keychain_delete_sbd()
424 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
425 kSecClass
, kSecClassGenericPassword
,
426 kSecAttrSynchronizable
, kCFBooleanTrue
,
427 kSecUseTombstones
, kCFBooleanFalse
,
428 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
431 result
= do_delete(item
);
437 void SOSAccountSetToNew(SOSAccountRef a
) {
438 secnotice("accountChange", "Setting Account to New");
441 CFReleaseNull(a
->my_identity
);
442 CFReleaseNull(a
->trusted_circle
);
443 CFReleaseNull(a
->trusted_rings
);
444 CFReleaseNull(a
->backups
);
445 CFReleaseNull(a
->retirees
);
447 CFReleaseNull(a
->user_key_parameters
);
448 CFReleaseNull(a
->user_public
);
449 CFReleaseNull(a
->previous_public
);
450 CFReleaseNull(a
->_user_private
);
451 CFReleaseNull(a
->_password_tmp
);
453 CFReleaseNull(a
->key_transport
);
454 CFReleaseNull(a
->circle_transport
);
455 CFReleaseNull(a
->kvs_message_transport
);
456 CFReleaseNull(a
->ids_message_transport
);
457 CFReleaseNull(a
->expansion
);
459 /* remove all syncable items */
460 result
= do_keychain_delete_aks_bags();
461 secdebug("set to new", "result for deleting aks bags: %d", result
);
463 result
= do_keychain_delete_identities();
464 secdebug("set to new", "result for deleting identities: %d", result
);
466 result
= do_keychain_delete_lakitu();
467 secdebug("set to new", "result for deleting lakitu: %d", result
);
469 result
= do_keychain_delete_sbd();
470 secdebug("set to new", "result for deleting sbd: %d", result
);
472 a
->user_public_trusted
= false;
473 a
->departure_code
= kSOSNeverAppliedToCircle
;
474 a
->user_private_timer
= 0;
475 a
->lock_notification_token
= 0;
481 // update_interest_block;
484 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
485 a
->circle_transport
= NULL
;
486 a
->kvs_message_transport
= NULL
;
487 a
->ids_message_transport
= NULL
;
489 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
490 a
->backups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
492 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
493 a
->expansion
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
495 SOSAccountEnsureFactoryCircles(a
); // Does rings too
497 SOSUpdateKeyInterest(a
);
501 static CFStringRef
SOSAccountCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
502 SOSAccountRef a
= (SOSAccountRef
) aObj
;
504 CFStringRef gestaltDescription
= CFDictionaryCopyCompactDescription(a
->gestalt
);
506 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a
, gestaltDescription
, a
->trusted_circle
, a
->my_identity
);
508 CFReleaseNull(gestaltDescription
);
513 CFStringRef
SOSAccountCreateCompactDescription(SOSAccountRef a
) {
515 CFStringRef gestaltDescription
= CFDictionaryCopySuperCompactDescription(a
->gestalt
);
517 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), gestaltDescription
);
519 CFReleaseNull(gestaltDescription
);
524 static Boolean
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
)
526 SOSAccountRef laccount
= (SOSAccountRef
) lhs
;
527 SOSAccountRef raccount
= (SOSAccountRef
) rhs
;
529 return CFEqualSafe(laccount
->gestalt
, raccount
->gestalt
)
530 && CFEqualSafe(laccount
->trusted_circle
, raccount
->trusted_circle
)
531 && CFEqualSafe(laccount
->trusted_rings
, raccount
->trusted_rings
)
532 && CFEqualSafe(laccount
->my_identity
, raccount
->my_identity
);
535 dispatch_queue_t
SOSAccountGetQueue(SOSAccountRef account
) {
536 return account
->queue
;
539 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account
){
540 account
->user_public_trusted
= true;
543 SOSFullPeerInfoRef
SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
)
545 return CFRetainSafe(account
->my_identity
);
548 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account
) {
550 __block CFErrorRef error
= NULL
;
552 if (!SOSAccountHasPublicKey(account
, &error
)) {
553 CFReleaseSafe(error
);
559 require_action_quiet(account
->my_identity
, xit
,
560 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account identity not set"), NULL
, &error
));
562 SOSTransportMessageIDSGetIDSDeviceID(account
);
564 require_action_quiet(account
->trusted_circle
, xit
,
565 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account trusted circle not set"), NULL
, &error
));
567 require_action_quiet(hasID
, xit
,
568 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Missing IDS device ID"), NULL
, &error
));
569 ok
= SOSCircleHasPeerWithID(account
->trusted_circle
,
570 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)), &error
);
573 secerror("sync with device failure: %@", error
);
575 CFReleaseSafe(error
);
579 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef peerID
) {
580 SOSPeerInfoRef mypi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
581 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(mypi
);
583 return myPeerID
&& CFEqualSafe(myPeerID
, peerID
);
586 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef
*error
)
589 __block
bool SyncingCompletedOverIDS
= true;
590 __block
bool SyncingCompletedOverKVS
= true;
591 __block CFErrorRef localError
= NULL
;
592 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
593 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
594 CFMutableArrayRef peerIds
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
596 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
597 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
600 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
601 if (!SOSAccountIsThisPeerIDMe(account
, SOSPeerInfoGetPeerID(peer
))) {
602 if (SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)) {
603 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
604 CFMutableDictionaryRef circleToIdsId
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
605 CFMutableArrayRef ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
606 CFArrayAppendValue(ids
, SOSPeerInfoGetPeerID(peer
));
607 CFDictionaryAddValue(circleToIdsId
, SOSCircleGetName(circle
), ids
);
608 SyncingCompletedOverIDS
= SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, circleToIdsId
, &localError
);
609 if(!SyncingCompletedOverIDS
){
610 secerror("Failed to sync over IDS, falling back to KVS");
611 SyncingCompletedOverIDS
= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToIdsId
, &localError
);
613 CFReleaseNull(circleToIdsId
);
615 CFArrayAppendValue(peerIds
, SOSPeerInfoGetPeerID(peer
));
619 if (CFArrayGetCount(peerIds
)) {
620 secnotice("KVS", "Syncing with KVS capable peers");
621 CFDictionarySetValue(circleToPeerIDs
, SOSCircleGetName(circle
), peerIds
);
622 SyncingCompletedOverKVS
&= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToPeerIDs
, &localError
);
625 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->kvs_message_transport
);
626 result
= SOSEngineSyncWithPeers(engine
, account
->ids_message_transport
, account
->kvs_message_transport
, &localError
);
628 result
&= ((SyncingCompletedOverIDS
) &&
629 (SyncingCompletedOverKVS
|| (CFDictionaryGetCount(circleToPeerIDs
) == 0)));
632 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
635 CFReleaseNull(circleToPeerIDs
);
638 secdebug("Account", "Could not sync with all peers: %@", localError
);
639 // Tell account to update SOSEngine with current trusted peers
640 if (isSOSErrorCoded(localError
, kSOSErrorPeerNotFound
)) {
641 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
642 account
->circle_rings_retirements_need_attention
= true;
644 CFErrorPropagate(localError
, error
);
647 CFReleaseNull(peerIds
);
648 CFReleaseSafe(localError
);
652 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
653 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
657 SOSPeerInfoRef myPeerInfo
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
658 require_action_quiet(account
->my_identity
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer")));
659 require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
);
661 CFStringRef cleanupPeerID
= SOSPeerInfoGetPeerID(cleanupPeer
);
663 CFStringRef circle_name
= SOSCircleGetName(circle
);
665 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
666 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
);
668 CFErrorRef localError
= NULL
;
669 if (!(success
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) {
670 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
673 if (account
->ids_message_transport
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) {
674 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
677 CFReleaseNull(localError
);
679 if((success
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) {
680 if (!(success
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) {
681 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
);
684 CFReleaseNull(localError
);
685 CFReleaseNull(circleToPeerIDs
);
691 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
692 CFMutableSetRef retirees_to_remove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
694 __block
bool success
= true;
696 CFSetForEach(account
->retirees
, ^(const void *value
) {
697 SOSPeerInfoRef retiree
= (SOSPeerInfoRef
) value
;
700 // 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.
701 if (!SOSPeerInfoIsRetirementTicket(retiree
) ||
702 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) {
703 CFSetAddValue(retirees_to_remove
, retiree
);
708 CFMutableArrayRef retirees_to_cleanup
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
709 CFSetForEach(retirees_to_remove
, ^(const void *value
) {
710 CFArrayAppendValue(retirees_to_cleanup
, value
);
711 CFSetRemoveValue(account
->retirees
, value
);
714 CFReleaseNull(retirees_to_remove
);
716 CFDictionaryRef retirements_to_remove
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
717 SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
,
720 CFReleaseNull(retirees_to_cleanup
);
722 success
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
);
724 CFReleaseNull(retirements_to_remove
);
729 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
) {
730 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
731 CFSetSetValue(account
->retirees
, peer
);
732 CFErrorRef cleanupError
= NULL
;
733 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) {
734 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
);
736 CFReleaseSafe(cleanupError
);
741 SOSCircleRef
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef
*error
) {
742 SOSCircleRef new_circle
= SOSCircleCopyCircle(NULL
, starting_circle
, error
);
743 if(!new_circle
) return NULL
;
745 if (account
->retirees
) {
746 CFSetForEach(account
->retirees
, ^(const void* value
) {
747 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
748 if (isSOSPeerInfo(pi
)) {
749 SOSCircleUpdatePeerInfo(new_circle
, pi
);
754 if(SOSCircleCountPeers(new_circle
) == 0) {
755 SOSCircleResetToEmpty(new_circle
, NULL
);
762 // MARK: Circle Membership change notificaion
765 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
766 SOSAccountCircleMembershipChangeBlock copy
= Block_copy(changeBlock
);
767 CFArrayAppendValue(a
->change_blocks
, copy
);
771 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
772 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
775 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) {
776 if (!changeBlock
) return;
778 CFRetainSafe(ds_name
);
779 SOSAccountCircleMembershipChangeBlock block_to_register
= ^void (SOSCircleRef new_circle
,
780 CFSetRef added_peers
, CFSetRef removed_peers
,
781 CFSetRef added_applicants
, CFSetRef removed_applicants
) {
783 if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
))
786 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
);
787 CFStringRef myPi_id
= myPi
? SOSPeerInfoGetPeerID(myPi
) : NULL
;
789 CFMutableArrayRef peer_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
790 CFMutableArrayRef added_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
791 CFMutableArrayRef removed_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
793 if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) {
794 SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) {
795 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
);
798 CFSetForEach(added_peers
, ^(const void *value
) {
799 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
802 CFSetForEach(removed_peers
, ^(const void *value
) {
803 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
807 if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
))
808 changeBlock(peer_ids
, added_ids
, removed_ids
);
810 CFReleaseSafe(peer_ids
);
811 CFReleaseSafe(added_ids
);
812 CFReleaseSafe(removed_ids
);
815 CFRetainSafe(changeBlock
);
816 SOSAccountAddChangeBlock(a
, block_to_register
);
818 CFSetRef empty
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
819 if (a
->trusted_circle
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) {
820 block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
);
822 CFReleaseSafe(empty
);
825 void SOSAccountPurgeIdentity(SOSAccountRef account
) {
826 if (account
->my_identity
) {
827 // Purge private key but don't return error if we can't.
828 CFErrorRef purgeError
= NULL
;
829 if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) {
830 secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
);
832 CFReleaseNull(purgeError
);
834 CFReleaseNull(account
->my_identity
);
838 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
839 SOSFullPeerInfoRef fpi
= account
->my_identity
;
840 if(!fpi
) return false;
842 CFErrorRef localError
= NULL
;
846 SOSPeerInfoRef retire_peer
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
);
848 secerror("Create ticket failed for peer %@: %@", fpi
, localError
);
850 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
851 if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) {
852 // Remove our application if we have one.
853 SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
);
854 } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) {
855 if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) {
856 CFErrorRef cleanupError
= NULL
;
857 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) {
858 secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
);
860 CFReleaseSafe(cleanupError
);
864 // Store the retirement record locally.
865 CFSetAddValue(account
->retirees
, retire_peer
);
867 // Write retirement to Transport
868 CFErrorRef postError
= NULL
;
869 if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){
870 secwarning("Couldn't post retirement (%@)", postError
);
872 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){
873 secwarning("Couldn't flush retirement data (%@)", postError
);
875 CFReleaseNull(postError
);
878 SOSAccountPurgeIdentity(account
);
882 CFReleaseNull(localError
);
883 CFReleaseNull(retire_peer
);
887 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) {
888 SOSFullPeerInfoRef fpi
= account
->my_identity
;
889 if(!fpi
) return false;
890 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
891 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
893 CFErrorRef localError
= NULL
;
896 bool writeRing
= false;
897 bool writePeerInfo
= false;
899 if(SOSRingHasPeerID(ring
, peerID
)) {
900 writePeerInfo
= true;
904 // this was circle behavior - at some point
905 if(SOSRingHasApplicant(ring
, peerID
)) {
910 if(writePeerInfo
|| writeRing
) {
911 SOSRingWithdraw(ring
, NULL
, fpi
, error
);
914 // Write leave thing to Transport
915 CFDataRef peerInfoData
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
);
916 SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors?
919 CFDataRef ring_data
= SOSRingCopyEncodedData(ring
, error
);
922 SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors?
924 CFReleaseNull(ring_data
);
927 CFReleaseNull(localError
);
931 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef
*error
) {
933 SOSTransportCircleRef transport
= account
->circle_transport
;
935 result
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
);
941 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
942 local value that has been overwritten by a distant value. If there is no
943 conflict between the local and the distant values when doing the initial
944 sync (e.g. if the cloud has no data stored or the client has not stored
945 any data yet), you'll never see that notification.
947 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
948 with server but initial round trip with server does not imply
949 NSUbiquitousKeyValueStoreInitialSyncChange.
954 // MARK: Status summary
957 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) {
959 return kSOSCCNotInCircle
;
961 if (circle
&& SOSCircleCountPeers(circle
) == 0)
962 return kSOSCCCircleAbsent
;
966 if(SOSPeerInfoIsRetirementTicket(this_peer
))
967 return kSOSCCNotInCircle
;
969 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
970 return kSOSCCInCircle
;
972 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
973 return kSOSCCRequestPending
;
976 return kSOSCCNotInCircle
;
979 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef
*error
) {
980 SOSCCStatus result
= SOSAccountGetCircleStatus(account
, error
);
982 if (result
!= kSOSCCInCircle
&& result
!= kSOSCCError
) {
983 SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle"));
990 SOSCCStatus
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) {
991 if (!SOSAccountHasPublicKey(account
, error
)) {
995 return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
));
999 // MARK: Account Reset Circles
1002 static bool SOSAccountResetCircleToOffering(SOSAccountRef account
, SecKeyRef user_key
, CFErrorRef
*error
) {
1003 bool result
= false;
1005 require(SOSAccountHasCircle(account
, error
), fail
);
1006 require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1008 (void) SOSAccountResetAllRings(account
, error
);
1010 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1011 bool result
= false;
1012 SOSFullPeerInfoRef cloud_identity
= NULL
;
1013 CFErrorRef localError
= NULL
;
1015 require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
);
1018 SOSPeerInfoRef cloud_peer
= GenerateNewCloudIdentityPeerInfo(error
);
1019 require_quiet(cloud_peer
, err_out
);
1020 cloud_identity
= CopyCloudKeychainIdentity(cloud_peer
, error
);
1021 CFReleaseNull(cloud_peer
);
1022 require_quiet(cloud_identity
, err_out
);
1025 account
->departure_code
= kSOSNeverLeftCircle
;
1026 require_quiet(SOSAccountAddEscrowToPeerInfo(account
, SOSAccountGetMyFullPeerInfo(account
), error
), err_out
);
1027 require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, &localError
), err_out
);
1028 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), &localError
), err_out
);
1030 SOSAccountPublishCloudParameters(account
, NULL
);
1033 if (result
== false)
1034 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
1035 if (localError
&& error
&& *error
== NULL
) {
1036 *error
= localError
;
1039 CFReleaseNull(localError
);
1040 CFReleaseNull(cloud_identity
);
1051 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) {
1052 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1056 CFReleaseNull(account
->my_identity
);
1058 return user_key
&& SOSAccountResetCircleToOffering(account
, user_key
, error
);
1061 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
1062 if (!SOSAccountHasPublicKey(account
, error
))
1064 __block
bool result
= true;
1066 result
&= SOSAccountResetAllRings(account
, error
);
1068 CFReleaseNull(account
->my_identity
);
1070 account
->departure_code
= kSOSWithdrewMembership
;
1071 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1072 result
= SOSCircleResetToEmpty(circle
, error
);
1077 secerror("error: %@", error
? *error
: NULL
);
1083 // MARK: start backups
1086 bool SOSAccountEnsureBackupStarts(SOSAccountRef account
) {
1088 __block
bool result
= false;
1089 __block CFErrorRef error
= NULL
;
1090 secnotice("backup", "Starting new backups");
1092 CFDataRef backupKey
= SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account
), sBackupKeyKey
);
1094 if (CFEqualSafe(backupKey
, account
->backup_key
)){
1095 CFReleaseNull(backupKey
);
1099 if(account
->backup_key
!= NULL
){
1100 require_quiet(SOSBSKBIsGoodBackupPublic(account
->backup_key
, &error
), exit
);
1101 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), &error
,
1102 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
1103 return SOSFullPeerInfoUpdateBackupKey(fpi
, account
->backup_key
, error
);
1105 CFErrorRef localError
= NULL
;
1106 if (!SOSDeleteV0Keybag(&localError
)) {
1107 secerror("Failed to delete v0 keybag: %@", localError
);
1109 CFReleaseNull(localError
);
1113 SOSAccountForEachBackupView(account
, ^(const void *value
) {
1114 CFStringRef viewName
= (CFStringRef
)value
;
1115 result
&= SOSAccountStartNewBackup(account
, viewName
, &error
);
1119 if(account
->backup_key
== NULL
){
1120 secerror("account backup key is NULL!");
1126 secnotice("backupkey", "Failed to setup backup public key: %@", error
? (CFTypeRef
) error
: (CFTypeRef
) CFSTR("No error space provided"));
1128 CFReleaseNull(backupKey
);
1133 // MARK: Waiting for in-sync
1136 static bool SOSAccountHasBeenInSync(SOSAccountRef account
) {
1137 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1138 CFSetRef unsynced
= asSet(unsyncedObject
, NULL
);
1140 return !(unsyncedObject
== kCFBooleanTrue
|| (unsynced
&& (CFSetGetCount(unsynced
) > 0)));
1143 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account
, CFSetRef viewsInSync
) {
1144 bool notifyOfChange
= false;
1146 SOSCCStatus circleStatus
= SOSAccountGetCircleStatus(account
, NULL
);
1147 bool inOrApplying
= (circleStatus
== kSOSCCInCircle
) || (circleStatus
== kSOSCCRequestPending
);
1149 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1151 if (!inOrApplying
) {
1152 if (unsyncedObject
!= NULL
) {
1153 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1154 secnotice("initial-sync", "in sync, clearing pending");
1155 notifyOfChange
= true;
1157 } else if (circleStatus
== kSOSCCInCircle
) {
1158 __block CFMutableSetRef viewsToSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1159 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1160 SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) {
1161 CFSetUnion(viewsToSync
, enabled
);
1166 CFSetSubtract(viewsToSync
, viewsInSync
);
1170 if (unsyncedObject
== kCFBooleanTrue
) {
1171 if (CFSetGetCount(viewsToSync
) == 0) {
1172 secnotice("initial-sync", "No views to wait for");
1173 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1175 __block CFSetRef newViews
= NULL
;
1176 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account
), ^(CFSetRef enabled
) {
1177 newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, enabled
, viewsToSync
);
1179 secnotice("initial-sync", "Pending views set from True: %@", newViews
);
1180 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1181 CFReleaseNull(newViews
);
1183 notifyOfChange
= true;
1184 } else if (isSet(unsyncedObject
)) {
1185 CFSetRef waiting
= (CFMutableSetRef
) unsyncedObject
;
1186 CFSetRef newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, waiting
, viewsToSync
);
1187 if (!CFEqualSafe(waiting
, newViews
)) {
1188 if (CFSetGetCount(newViews
) == 0) {
1189 secnotice("initial-sync", "No views left to wait for.");
1190 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1192 secnotice("initial-sync", "Pending views updated: %@", newViews
);
1193 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1195 notifyOfChange
= true;
1197 CFReleaseNull(newViews
);
1200 CFReleaseNull(viewsToSync
);
1203 if (notifyOfChange
) {
1204 if(SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
) == NULL
){
1205 CFDictionaryRef syncBlocks
= account
->waitForInitialSync_blocks
;
1206 account
->waitForInitialSync_blocks
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1208 CFDictionaryForEach(syncBlocks
, ^(const void *key
, const void *value
) {
1209 secnotice("updates", "calling in sync block [%@]", key
);
1210 ((SOSAccountWaitForInitialSyncBlock
)value
)(account
);
1213 CFReleaseNull(syncBlocks
);
1216 // Make sure we update the engine
1217 account
->circle_rings_retirements_need_attention
= true;
1220 return SOSAccountHasBeenInSync(account
);
1223 static void SOSAccountPeerGotInSync(SOSAccountRef account
, CFStringRef peerID
) {
1224 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID
);
1226 if (account
->trusted_circle
) {
1227 SOSPeerInfoRef peer
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
);
1229 CFSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
1230 SOSAccountUpdateOutOfSyncViews(account
, views
);
1231 CFReleaseNull(views
);
1233 CFReleaseNull(peer
);
1237 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account
) {
1238 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1240 CFDictionaryForEach(account
->notification_cleanups
, ^(const void *key
, const void *value
) {
1242 SOSEngineSetSyncCompleteListener(engine
, key
, NULL
);
1244 dispatch_async(account
->queue
, value
);
1247 CFDictionaryRemoveAllValues(account
->notification_cleanups
);
1250 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account
, CFStringRef peerID
) {
1251 dispatch_block_t cleanup
= CFDictionaryGetValue(account
->notification_cleanups
, peerID
);
1254 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1257 SOSEngineSetSyncCompleteListener(engine
, peerID
, NULL
);
1260 dispatch_async(account
->queue
, cleanup
);
1263 CFDictionaryRemoveValue(account
->notification_cleanups
, peerID
);
1267 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account
, CFStringRef peerID
, dispatch_block_t block
) {
1268 dispatch_block_t copy
= Block_copy(block
);
1269 CFDictionarySetValue(account
->notification_cleanups
, peerID
, copy
);
1270 CFReleaseNull(copy
);
1273 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) {
1274 if (CFDictionaryGetCount(account
->notification_cleanups
) == 0) {
1275 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
1276 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1278 SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
);
1281 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1282 CFStringRef peerID
= CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerID(peer
));
1284 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID
);
1285 SOSAccountRegisterCleanupBlock(account
, peerID
, ^{
1286 CFReleaseSafe(peerID
);
1289 SOSEngineSetSyncCompleteListener(engine
, peerID
, ^{
1290 SOSAccountPeerGotInSync(account
, peerID
);
1291 SOSAccountCleanupNotificationForPeer(account
, peerID
);
1292 SOSAccountFinishTransaction(account
);
1296 secerror("Couldn't find engine to setup notifications!!!");
1301 void SOSAccountCancelSyncChecking(SOSAccountRef account
) {
1302 SOSAccountCleanupNotificationForAllPeers(account
);
1303 SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1306 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account
) {
1307 bool hasBeenInSync
= false;
1309 if (!SOSAccountIsInCircle(account
, NULL
)) {
1310 SOSAccountCancelSyncChecking(account
);
1312 hasBeenInSync
= SOSAccountHasBeenInSync(account
);
1313 if (!hasBeenInSync
) {
1314 hasBeenInSync
= SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1315 if (hasBeenInSync
) {
1316 // Cancel and declare victory
1318 SOSAccountCancelSyncChecking(account
);
1320 // Make sure we're watching in case this is the fist attempt
1321 SOSAccountEnsureSyncChecking(account
);
1326 return hasBeenInSync
;
1333 static bool SOSAccountJoinCircle(SOSAccountRef account
, SecKeyRef user_key
,
1334 bool use_cloud_peer
, CFErrorRef
* error
) {
1335 __block
bool result
= false;
1336 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1338 require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???")));
1339 require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1341 SOSFullPeerInfoRef myCirclePeer
= account
->my_identity
;
1343 if (use_cloud_peer
) {
1344 cloud_full_peer
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
);
1346 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1349 if (SOSCircleCountPeers(account
->trusted_circle
) == 0) {
1350 result
= SOSAccountResetCircleToOffering(account
, user_key
, error
);
1352 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1353 result
= SOSAccountAddEscrowToPeerInfo(account
, myCirclePeer
, error
);
1354 result
&= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
);
1355 account
->departure_code
= kSOSNeverLeftCircle
;
1356 if(result
&& cloud_full_peer
) {
1357 CFErrorRef localError
= NULL
;
1358 CFStringRef cloudid
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
));
1359 require_quiet(cloudid
, finish
);
1360 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
);
1361 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
);
1364 secerror("Failed to join with cloud identity: %@", localError
);
1365 CFReleaseNull(localError
);
1373 CFReleaseNull(cloud_full_peer
);
1377 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) {
1378 bool success
= false;
1380 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1381 require_quiet(user_key
, done
); // Fail if we don't get one.
1383 require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join")));
1385 if (account
->my_identity
!= NULL
) {
1386 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1387 success
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
);
1388 require_quiet(!success
, done
);
1390 SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now.
1392 if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) {
1393 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
));
1395 CFReleaseNull(account
->my_identity
);
1400 success
= SOSAccountJoinCircle(account
, user_key
, use_cloud_identity
, error
);
1402 require_quiet(success
, done
);
1404 account
->departure_code
= kSOSNeverLeftCircle
;
1410 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) {
1411 return SOSAccountJoinCircles_internal(account
, false, error
);
1414 CFStringRef
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef
*error
){
1415 CFStringRef result
= NULL
;
1417 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1419 result
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1425 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){
1428 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
){
1429 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1430 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1431 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1433 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1435 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1436 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDS
, error
);
1437 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1439 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1446 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1447 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1448 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1450 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1452 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1453 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeKVS
, error
);
1454 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1456 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1464 SOSCCSyncWithAllPeers();
1471 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1473 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1474 //construct message dictionary, circle -> peerID -> message
1476 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1477 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1479 char *messageCharStar
;
1480 asprintf(&messageCharStar
, "%d", kIDSSendOneMessage
);
1481 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1483 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1485 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1486 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
)))
1487 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1490 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1491 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1493 CFReleaseNull(mutableDictionary
);
1494 CFReleaseNull(peerToMessage
);
1495 CFReleaseNull(circleToPeerMessages
);
1496 CFReleaseNull(messageString
);
1497 free(messageCharStar
);
1502 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1503 bool result
= false;
1504 //construct message dictionary, circle -> peerID -> message
1506 if(account
->ids_message_transport
== NULL
)
1507 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1509 require_quiet(account
->ids_message_transport
, fail
);
1510 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1511 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1513 char *messageCharStar
;
1514 asprintf(&messageCharStar
, "%d", kIDSStartPingTestMessage
);
1515 CFStringRef messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
1517 CFMutableDictionaryRef mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("send IDS test message"), NULL
);
1519 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1520 if(CFStringCompare(SOSAccountGetMyPeerID(account
), SOSPeerInfoGetPeerID(peer
), 0) != 0)
1521 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1524 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1525 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1527 CFReleaseNull(mutableDictionary
);
1528 CFReleaseNull(peerToMessage
);
1529 CFReleaseNull(circleToPeerMessages
);
1530 CFReleaseNull(messageString
);
1531 free(messageCharStar
);
1536 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef
*error
){
1538 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1540 __block
bool success
= true;
1541 __block CFErrorRef localError
= NULL
;
1542 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
1543 dispatch_retain(wait_for
); // Both this scope and the block own it
1545 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1546 success
= (sync_error
== NULL
);
1548 CFRetainAssign(localError
, sync_error
);
1551 dispatch_semaphore_signal(wait_for
);
1552 dispatch_release(wait_for
);
1555 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
1556 dispatch_release(wait_for
);
1558 if(!success
&& localError
!= NULL
&& error
!= NULL
){
1559 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1560 *error
= localError
;
1563 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1569 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) {
1570 return SOSAccountJoinCircles_internal(account
, true, error
);
1574 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
)
1578 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1579 return sosAccountLeaveCircle(account
, circle
, error
);
1582 account
->departure_code
= kSOSWithdrewMembership
;
1587 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
* error
)
1589 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1595 CFMutableSetRef peersToRemove
= CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault
, peers
);
1597 bool leaveCircle
= CFSetContainsValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1599 CFSetRemoveValue(peersToRemove
, SOSAccountGetMyPeerInfo(account
));
1601 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1602 bool success
= false;
1604 if(CFSetGetCount(peersToRemove
) != 0) {
1605 require_quiet(SOSCircleRemovePeers(circle
, user_key
, SOSAccountGetMyFullPeerInfo(account
), peersToRemove
, error
), done
);
1606 success
= SOSAccountGenerationSignatureUpdate(account
, error
);
1607 } else success
= true;
1609 if (success
&& leaveCircle
) {
1610 success
= sosAccountLeaveCircle(account
, circle
, error
);
1622 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) {
1623 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
1624 dispatch_group_t group
= dispatch_group_create();
1625 __block
bool result
= false;
1626 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
);
1627 // Add a task to the group
1628 dispatch_group_async(group
, queue
, ^{
1629 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1630 return sosAccountLeaveCircle(account
, circle
, error
);
1633 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
1634 dispatch_group_wait(group
, milestone
);
1636 account
->departure_code
= kSOSWithdrewMembership
;
1638 dispatch_release(group
);
1644 // MARK: Application
1647 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
,
1648 bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) {
1649 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1650 CFErrorRef peer_error
= NULL
;
1651 if (account
->trusted_circle
&& me
&&
1652 SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) {
1653 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) {
1654 __block
bool modified
= false;
1655 CFArrayForEach(peer_infos
, ^(const void *value
) {
1656 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1657 if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) {
1658 if (action(circle
, account
->my_identity
, peer
)) {
1667 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
1668 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
1671 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1672 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1676 __block
bool success
= true;
1677 __block
int64_t num_peers
= 0;
1679 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1680 bool accepted
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
);
1684 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1691 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1692 __block
bool success
= true;
1693 __block
int64_t num_peers
= 0;
1695 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1696 bool rejected
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
);
1700 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1708 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) {
1709 return CFSTR("We're compatible, go away");
1712 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) {
1713 return account
->departure_code
;
1716 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) {
1717 account
->departure_code
= reason
;
1721 CFArrayRef
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef
*error
) {
1722 CFArrayRef result
= NULL
;
1723 CFNumberRef generation
= NULL
;
1725 require_quiet(SOSAccountHasPublicKey(account
, error
), fail
);
1726 require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle")));
1728 generation
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
);
1729 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
);
1735 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef
*error
) {
1736 if (!SOSAccountHasPublicKey(account
, error
))
1739 return account
->user_public_trusted
;
1742 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef
*error
) {
1743 // TODO: this result is never set or used
1746 secnotice("updates", "Ensuring peer registration.");
1748 require_quiet(account
->trusted_circle
, done
);
1749 require_quiet(account
->my_identity
, done
);
1750 // If we are not in the circle, there is no point in setting up peers
1751 require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
);
1753 // This code only uses the SOSFullPeerInfoRef for two things:
1754 // - Finding out if this device is in the trusted circle
1755 // - Using the peerID for this device to see if the current peer is "me"
1756 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1758 CFStringRef my_id
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1760 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1761 if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) {
1762 CFErrorRef localError
= NULL
;
1763 SOSTransportMessageRef messageTransport
= NULL
;
1765 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1766 messageTransport
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport
: account
->kvs_message_transport
;
1769 messageTransport
= account
->kvs_message_transport
;
1771 SOSPeerCoderInitializeForPeer(messageTransport
->engine
, account
->my_identity
, peer
, &localError
);
1773 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
);
1774 CFReleaseSafe(localError
);
1778 //Initialize our device ID
1779 SOSTransportMessageIDSGetIDSDeviceID(account
);
1786 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef
*error
) {
1787 if (!account
->expansion
) {
1788 account
->expansion
= CFDictionaryCreateMutableForCFTypes(NULL
);
1791 return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary"));
1794 bool SOSAccountClearValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1795 bool success
= SOSAccountEnsureExpansion(account
, error
);
1796 require_quiet(success
, errOut
);
1798 CFDictionaryRemoveValue(account
->expansion
, key
);
1803 bool SOSAccountSetValue(SOSAccountRef account
, const void *key
, const void *value
, CFErrorRef
*error
) {
1804 bool success
= SOSAccountEnsureExpansion(account
, error
);
1805 require_quiet(success
, errOut
);
1807 CFDictionarySetValue(account
->expansion
, key
, value
);
1813 const void *SOSAccountGetValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1814 if (!account
->expansion
) {
1817 return CFDictionaryGetValue(account
->expansion
, key
);
1820 bool SOSAccountAddEscrowRecords(SOSAccountRef account
, CFStringRef dsid
, CFDictionaryRef record
, CFErrorRef
*error
){
1821 CFMutableDictionaryRef escrowRecords
= (CFMutableDictionaryRef
)SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1822 CFMutableDictionaryRef escrowCopied
= NULL
;
1823 bool success
= false;
1825 if(isDictionary(escrowRecords
) && escrowRecords
!= NULL
)
1826 escrowCopied
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(escrowRecords
), escrowRecords
);
1828 escrowCopied
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1830 CFDictionaryAddValue(escrowCopied
, dsid
, record
);
1831 SOSAccountSetValue(account
, kSOSEscrowRecord
, escrowCopied
, error
);
1836 CFReleaseNull(escrowCopied
);
1842 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account
, SOSFullPeerInfoRef myPeer
, CFErrorRef
*error
){
1843 bool success
= false;
1845 CFDictionaryRef escrowRecords
= SOSAccountGetValue(account
, kSOSEscrowRecord
, error
);
1846 success
= SOSFullPeerInfoReplaceEscrowRecords(myPeer
, escrowRecords
, error
);
1851 bool SOSAccountCheckPeerAvailability(SOSAccountRef account
, CFErrorRef
*error
)
1853 CFMutableDictionaryRef circleToPeerMessages
= NULL
;
1854 CFStringRef messageString
= NULL
;
1855 CFMutableDictionaryRef mutableDictionary
= NULL
;
1856 CFMutableSetRef peers
= NULL
;
1857 CFMutableDictionaryRef peerList
= NULL
;
1858 char* message
= NULL
;
1859 bool result
= false;
1860 if(account
->ids_message_transport
== NULL
)
1861 account
->ids_message_transport
= (SOSTransportMessageRef
)SOSTransportMessageIDSCreate(account
, SOSCircleGetName(account
->trusted_circle
), error
);
1863 require_quiet(account
->ids_message_transport
, fail
);
1864 circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1866 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
1868 asprintf(&message
, "%d", kIDSPeerAvailability
);
1869 messageString
= CFStringCreateWithCString(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
);
1871 mutableDictionary
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, messageString
, CFSTR("checking peers"), NULL
);
1873 //make sure there are peers in the circle
1874 peers
= SOSCircleCopyPeers(account
->trusted_circle
, kCFAllocatorDefault
);
1875 require_quiet(CFSetGetCount(peers
) > 0, fail
);
1876 CFReleaseNull(peers
);
1878 peerList
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1879 SOSCircleRef circle
= account
->trusted_circle
;
1881 //check each peer to make sure they have the right view set enabled
1882 CFSetRef mySubSet
= SOSViewsGetV0SubviewSet();
1883 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
1884 if(!CFEqualSafe(peer
, SOSAccountGetMyPeerInfo(account
))){
1885 CFMutableSetRef peerViews
= SOSPeerInfoCopyEnabledViews(peer
);
1886 CFSetRef intersectSets
= CFSetCreateIntersection(kCFAllocatorDefault
, mySubSet
, peerViews
);
1887 if(CFEqualSafe(intersectSets
, mySubSet
)){
1888 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
1889 if(deviceID
!= NULL
)
1890 CFDictionaryAddValue(peerList
, SOSPeerInfoGetPeerID(peer
), mutableDictionary
);
1891 CFReleaseNull(deviceID
);
1893 CFReleaseNull(peerViews
);
1894 CFReleaseNull(intersectSets
);
1898 require_quiet(CFDictionaryGetCount(peerList
) > 0 , fail
);
1899 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerList
);
1900 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1903 CFReleaseNull(mutableDictionary
);
1904 CFReleaseNull(messageString
);
1905 CFReleaseNull(peerList
);
1906 CFReleaseNull(circleToPeerMessages
);
1907 CFReleaseNull(peers
);
1913 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account
) {
1914 if (!SOSAccountIsInCircle(account
, NULL
))
1917 SOSAccountModifyCircle(account
, NULL
, ^bool (SOSCircleRef circle
) {
1918 __block
bool updated
= false;
1919 CFSetForEach(account
->retirees
, ^(CFTypeRef element
){
1920 SOSPeerInfoRef retiree
= asSOSPeerInfo(element
);
1922 if (retiree
&& SOSCircleUpdatePeerInfo(circle
, retiree
)) {
1924 secnotice("retirement", "Updated retired peer %@ in %@", retiree
, circle
);
1925 CFErrorRef cleanupError
= NULL
;
1926 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retiree
, &cleanupError
))
1927 secerror("Error cleanup up after peer (%@): %@", retiree
, cleanupError
);
1928 CFReleaseSafe(cleanupError
);
1935 void SOSAccountFinishTransaction(SOSAccountRef account
) {
1936 if(account
->circle_rings_retirements_need_attention
){
1937 SOSAccountRecordRetiredPeersInCircle(account
);
1939 CFErrorRef localError
= NULL
;
1940 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &localError
)) {
1941 secerror("flush circle failed %@", localError
);
1943 CFReleaseSafe(localError
);
1945 SOSAccountNotifyEngines(account
); // For now our only rings are backup rings.
1948 SOSAccountCheckHasBeenInSync(account
);
1950 account
->circle_rings_retirements_need_attention
= false;