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");
36 const CFStringRef kSOSUnsyncedViewsKey
= CFSTR("unsynced");
39 #define DATE_LENGTH 25
40 const CFStringRef kSOSAccountDebugScope
= CFSTR("Scope");
43 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a
)
46 CFStringRef circle_name
= NULL
;
48 require_quiet(a
, xit
);
49 require_quiet(a
->factory
, xit
);
51 circle_name
= SOSDataSourceFactoryCopyName(a
->factory
);
52 require(circle_name
, xit
);
54 SOSAccountEnsureCircle(a
, circle_name
, NULL
);
59 // We don't own name, so don't release it.
60 CFReleaseNull(circle_name
);
65 SOSAccountRef
SOSAccountCreateBasic(CFAllocatorRef allocator
,
66 CFDictionaryRef gestalt
,
67 SOSDataSourceFactoryRef factory
) {
68 SOSAccountRef a
= CFTypeAllocate(SOSAccount
, struct __OpaqueSOSAccount
, allocator
);
70 a
->queue
= dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL
);
72 a
->notification_cleanups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
74 a
->gestalt
= CFRetainSafe(gestalt
);
76 a
->trusted_circle
= NULL
;
77 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(allocator
);
78 a
->backups
= CFDictionaryCreateMutableForCFTypes(allocator
);
79 a
->my_identity
= NULL
;
80 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(allocator
);
82 a
->factory
= factory
; // We adopt the factory. kthanksbai.
84 a
->_user_private
= NULL
;
85 a
->_password_tmp
= NULL
;
86 a
->user_private_timer
= NULL
;
88 a
->change_blocks
= CFArrayCreateMutableForCFTypes(allocator
);
90 a
->departure_code
= kSOSNeverAppliedToCircle
;
92 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
93 a
->circle_transport
= NULL
;
94 a
->kvs_message_transport
= NULL
;
95 a
->ids_message_transport
= NULL
;
96 a
->expansion
= CFDictionaryCreateMutableForCFTypes(allocator
);
101 SOSSecurityPropertyResultCode
SOSAccountUpdateSecurityProperty(SOSAccountRef account
, CFStringRef property
, SOSSecurityPropertyActionCode actionCode
, CFErrorRef
*error
) {
102 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralSecurityPropertyError
;
103 bool updateCircle
= false;
104 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
105 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
106 retval
= SOSFullPeerInfoUpdateSecurityProperty(account
->my_identity
, actionCode
, property
, error
);
108 if(actionCode
== kSOSCCSecurityPropertyEnable
&& retval
== kSOSCCSecurityPropertyValid
) {
110 } else if(actionCode
== kSOSCCSecurityPropertyDisable
&& retval
== kSOSCCSecurityPropertyNotValid
) {
112 } else if(actionCode
== kSOSCCSecurityPropertyPending
) {
117 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
118 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
119 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
127 SOSSecurityPropertyResultCode
SOSAccountSecurityPropertyStatus(SOSAccountRef account
, CFStringRef property
, CFErrorRef
*error
) {
128 SOSSecurityPropertyResultCode retval
= kSOSCCGeneralViewError
;
129 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
130 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
131 retval
= SOSFullPeerInfoSecurityPropertyStatus(account
->my_identity
, property
, error
);
136 bool SOSAccountUpdateGestalt(SOSAccountRef account
, CFDictionaryRef new_gestalt
)
138 if (CFEqualSafe(new_gestalt
, account
->gestalt
))
141 if (account
->trusted_circle
&& account
->my_identity
142 && SOSFullPeerInfoUpdateGestalt(account
->my_identity
, new_gestalt
, NULL
)) {
143 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
144 secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
145 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
149 CFRetainAssign(account
->gestalt
, new_gestalt
);
153 bool SOSAccountUpdateDSID(SOSAccountRef account
, CFStringRef dsid
){
154 SOSAccountSetValue(account
, kSOSDSIDKey
, dsid
, NULL
);
155 //send new DSID over account changed
156 SOSTransportCircleSendOfficialDSID(account
->circle_transport
, dsid
, NULL
);
161 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account
, CFSetRef minimumViews
) {
162 if (account
->trusted_circle
&& account
->my_identity
) {
163 if(SOSFullPeerInfoUpdateToCurrent(account
->my_identity
, minimumViews
)) {
164 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
165 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
166 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
174 SOSViewResultCode
SOSAccountUpdateView(SOSAccountRef account
, CFStringRef viewname
, SOSViewActionCode actionCode
, CFErrorRef
*error
) {
175 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
176 SOSViewResultCode currentStatus
= kSOSCCGeneralViewError
;
177 bool updateCircle
= false;
178 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
179 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
180 require_action_quiet((actionCode
== kSOSCCViewEnable
) || (actionCode
== kSOSCCViewDisable
), errOut
, CFSTR("Invalid View Action"));
181 currentStatus
= SOSAccountViewStatus(account
, viewname
, error
);
182 require_action_quiet((currentStatus
== kSOSCCViewNotMember
) || (currentStatus
== kSOSCCViewMember
), errOut
, CFSTR("View Membership Not Actionable"));
184 if (CFEqualSafe(viewname
, kSOSViewKeychainV0
)) {
185 // The V0 view switches on and off all on it's own, we allow people the delusion
186 // of control and status if it's what we're stuck at., otherwise error.
187 if (SOSAccountSyncingV0(account
)) {
188 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Can't disable V0 view and it's on right now"));
189 retval
= kSOSCCViewMember
;
191 require_action_quiet(actionCode
= kSOSCCViewEnable
, errOut
, CFSTR("Can't enable V0 and it's off right now"));
192 retval
= kSOSCCViewNotMember
;
194 } else if (SOSAccountSyncingV0(account
) && SOSViewsIsV0Subview(viewname
)) {
195 // Subviews of V0 syncing can't be turned off if V0 is on.
196 require_action_quiet(actionCode
= kSOSCCViewDisable
, errOut
, CFSTR("Have V0 peer can't disable"));
197 retval
= kSOSCCViewMember
;
199 if(actionCode
== kSOSCCViewEnable
&& currentStatus
== kSOSCCViewNotMember
) {
200 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
201 if(retval
== kSOSCCViewMember
) updateCircle
= true;
202 } else if(actionCode
== kSOSCCViewDisable
&& currentStatus
== kSOSCCViewMember
) {
203 retval
= SOSFullPeerInfoUpdateViews(account
->my_identity
, actionCode
, viewname
, error
);
204 if(retval
== kSOSCCViewNotMember
) updateCircle
= true;
206 retval
= currentStatus
;
210 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
211 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
212 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
221 SOSViewResultCode
SOSAccountViewStatus(SOSAccountRef account
, CFStringRef viewname
, CFErrorRef
*error
) {
222 SOSViewResultCode retval
= kSOSCCGeneralViewError
;
223 require_action_quiet(account
->trusted_circle
, errOut
, SOSCreateError(kSOSErrorNoCircle
, CFSTR("No Trusted Circle"), NULL
, error
));
224 require_action_quiet(account
->my_identity
, errOut
, SOSCreateError(kSOSErrorPeerNotFound
, CFSTR("No Peer for Account"), NULL
, error
));
225 retval
= SOSFullPeerInfoViewStatus(account
->my_identity
, viewname
, error
);
230 static void dumpViewSet(CFStringRef label
, CFSetRef views
) {
232 secnotice("circleChange", "%@ list: %@", label
, views
);
234 secnotice("circleChange", "No %@ list provided.", label
);
238 bool SOSAccountUpdateViewSets(SOSAccountRef account
, CFSetRef enabledViews
, CFSetRef disabledViews
) {
239 bool updateCircle
= false;
240 dumpViewSet(CFSTR("Enabled"), enabledViews
);
241 dumpViewSet(CFSTR("Disabled"), disabledViews
);
243 require_action_quiet(account
->trusted_circle
, errOut
, secnotice("views", "Attempt to set viewsets with no trusted circle"));
244 require_action_quiet(account
->my_identity
, errOut
, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
245 require_action_quiet(enabledViews
|| disabledViews
, errOut
, secnotice("views", "No work to do"));
248 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
249 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, SOSFullPeerInfoGetPeerInfo(fpi
), NULL
);
251 require_action_quiet(pi
, errOut
, secnotice("views", "Couldn't copy PeerInfoRef"));
254 if(!SOSPeerInfoVersionIsCurrent(pi
)) {
255 if(!SOSPeerInfoUpdateToV2(pi
, NULL
)) {
256 secnotice("views", "Unable to update peer to V2- can't update views");
261 if(enabledViews
) updateCircle
= SOSViewSetEnable(pi
, enabledViews
);
262 if(disabledViews
) updateCircle
|= SOSViewSetDisable(pi
, disabledViews
);
264 /* UPDATE FULLPEERINFO VIEWS */
266 if (updateCircle
&& SOSFullPeerInfoUpdateToThisPeer(fpi
, pi
, NULL
)) {
267 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle_to_change
) {
268 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
269 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
278 SOSAccountRef
SOSAccountCreate(CFAllocatorRef allocator
,
279 CFDictionaryRef gestalt
,
280 SOSDataSourceFactoryRef factory
) {
281 SOSAccountRef a
= SOSAccountCreateBasic(allocator
, gestalt
, factory
);
284 SOSAccountEnsureFactoryCircles(a
);
289 static void SOSAccountDestroy(CFTypeRef aObj
) {
290 SOSAccountRef a
= (SOSAccountRef
) aObj
;
292 // We don't own the factory, merely have a reference to the singleton
296 SOSAccountCleanupNotificationForAllPeers(a
);
298 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(a
->factory
, SOSCircleGetName(a
->trusted_circle
), NULL
);
301 SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
);
303 dispatch_sync(a
->queue
, ^{
304 CFReleaseNull(a
->gestalt
);
306 CFReleaseNull(a
->my_identity
);
307 CFReleaseNull(a
->trusted_circle
);
308 CFReleaseNull(a
->trusted_rings
);
309 CFReleaseNull(a
->backups
);
310 CFReleaseNull(a
->retirees
);
312 a
->user_public_trusted
= false;
313 CFReleaseNull(a
->user_public
);
314 CFReleaseNull(a
->user_key_parameters
);
316 SOSAccountPurgePrivateCredential(a
);
317 CFReleaseNull(a
->previous_public
);
318 CFReleaseNull(a
->_user_private
);
319 CFReleaseNull(a
->_password_tmp
);
321 a
->departure_code
= kSOSNeverAppliedToCircle
;
322 CFReleaseNull(a
->kvs_message_transport
);
323 CFReleaseNull(a
->ids_message_transport
);
324 CFReleaseNull(a
->key_transport
);
325 CFReleaseNull(a
->circle_transport
);
326 dispatch_release(a
->queue
);
327 CFReleaseNull(a
->notification_cleanups
);
329 dispatch_release(a
->user_private_timer
);
330 CFReleaseNull(a
->change_blocks
);
331 CFReleaseNull(a
->expansion
);
336 static OSStatus
do_delete(CFDictionaryRef query
) {
339 result
= SecItemDelete(query
);
341 secerror("SecItemDelete: %d", (int)result
);
347 do_keychain_delete_aks_bags()
350 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
351 kSecClass
, kSecClassGenericPassword
,
352 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
353 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
354 kSecAttrService
, CFSTR("SecureBackupService"),
355 kSecAttrSynchronizable
, kCFBooleanTrue
,
356 kSecUseTombstones
, kCFBooleanFalse
,
359 result
= do_delete(item
);
366 do_keychain_delete_identities()
369 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
370 kSecClass
, kSecClassKey
,
371 kSecAttrSynchronizable
, kCFBooleanTrue
,
372 kSecUseTombstones
, kCFBooleanFalse
,
373 kSecAttrAccessGroup
, CFSTR("com.apple.security.sos"),
376 result
= do_delete(item
);
383 do_keychain_delete_lakitu()
386 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
387 kSecClass
, kSecClassGenericPassword
,
388 kSecAttrSynchronizable
, kCFBooleanTrue
,
389 kSecUseTombstones
, kCFBooleanFalse
,
390 kSecAttrAccessGroup
, CFSTR("com.apple.lakitu"),
391 kSecAttrAccount
, CFSTR("EscrowServiceBypassToken"),
392 kSecAttrService
, CFSTR("EscrowService"),
395 result
= do_delete(item
);
402 do_keychain_delete_sbd()
405 CFDictionaryRef item
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
406 kSecClass
, kSecClassGenericPassword
,
407 kSecAttrSynchronizable
, kCFBooleanTrue
,
408 kSecUseTombstones
, kCFBooleanFalse
,
409 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
412 result
= do_delete(item
);
418 void SOSAccountSetToNew(SOSAccountRef a
) {
419 secnotice("accountChange", "Setting Account to New");
422 CFReleaseNull(a
->my_identity
);
423 CFReleaseNull(a
->trusted_circle
);
424 CFReleaseNull(a
->trusted_rings
);
425 CFReleaseNull(a
->backups
);
426 CFReleaseNull(a
->retirees
);
428 CFReleaseNull(a
->user_key_parameters
);
429 CFReleaseNull(a
->user_public
);
430 CFReleaseNull(a
->previous_public
);
431 CFReleaseNull(a
->_user_private
);
432 CFReleaseNull(a
->_password_tmp
);
434 CFReleaseNull(a
->key_transport
);
435 CFReleaseNull(a
->circle_transport
);
436 CFReleaseNull(a
->kvs_message_transport
);
437 CFReleaseNull(a
->ids_message_transport
);
438 CFReleaseNull(a
->expansion
);
440 /* remove all syncable items */
441 result
= do_keychain_delete_aks_bags();
442 secdebug("set to new", "result for deleting aks bags: %d", result
);
444 result
= do_keychain_delete_identities();
445 secdebug("set to new", "result for deleting identities: %d", result
);
447 result
= do_keychain_delete_lakitu();
448 secdebug("set to new", "result for deleting lakitu: %d", result
);
450 result
= do_keychain_delete_sbd();
451 secdebug("set to new", "result for deleting sbd: %d", result
);
453 a
->user_public_trusted
= false;
454 a
->departure_code
= kSOSNeverAppliedToCircle
;
455 a
->user_private_timer
= 0;
456 a
->lock_notification_token
= 0;
462 // update_interest_block;
465 a
->key_transport
= (SOSTransportKeyParameterRef
)SOSTransportKeyParameterKVSCreate(a
, NULL
);
466 a
->circle_transport
= NULL
;
467 a
->kvs_message_transport
= NULL
;
468 a
->ids_message_transport
= NULL
;
470 a
->trusted_rings
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
471 a
->backups
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
473 a
->retirees
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
474 a
->expansion
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
476 SOSAccountEnsureFactoryCircles(a
); // Does rings too
480 static CFStringRef
SOSAccountCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
481 SOSAccountRef a
= (SOSAccountRef
) aObj
;
483 CFStringRef gestaltDescription
= CFDictionaryCopyCompactDescription(a
->gestalt
);
485 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a
, gestaltDescription
, a
->trusted_circle
, a
->my_identity
);
487 CFReleaseNull(gestaltDescription
);
492 CFStringRef
SOSAccountCreateCompactDescription(SOSAccountRef a
) {
494 CFStringRef gestaltDescription
= CFDictionaryCopySuperCompactDescription(a
->gestalt
);
496 CFStringRef result
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), gestaltDescription
);
498 CFReleaseNull(gestaltDescription
);
503 static Boolean
SOSAccountCompare(CFTypeRef lhs
, CFTypeRef rhs
)
505 SOSAccountRef laccount
= (SOSAccountRef
) lhs
;
506 SOSAccountRef raccount
= (SOSAccountRef
) rhs
;
508 return CFEqualSafe(laccount
->gestalt
, raccount
->gestalt
)
509 && CFEqualSafe(laccount
->trusted_circle
, raccount
->trusted_circle
)
510 && CFEqualSafe(laccount
->trusted_rings
, raccount
->trusted_rings
)
511 && CFEqualSafe(laccount
->my_identity
, raccount
->my_identity
);
514 dispatch_queue_t
SOSAccountGetQueue(SOSAccountRef account
) {
515 return account
->queue
;
518 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account
){
519 account
->user_public_trusted
= true;
522 SOSFullPeerInfoRef
SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account
, CFAllocatorRef allocator
, CFErrorRef
* error
)
524 return CFRetainSafe(account
->my_identity
);
527 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account
) {
529 __block CFErrorRef error
= NULL
;
531 if (!SOSAccountHasPublicKey(account
, &error
)) {
532 CFReleaseSafe(error
);
538 require_action_quiet(account
->my_identity
, xit
,
539 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account identity not set"), NULL
, &error
));
541 if ((whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
) && account
->ids_message_transport
) {
542 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
543 if(deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
545 secerror("Cannot sync with all peers at this time, securityd needs the IDS device ID first.");
547 __block
bool success
= false;
549 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
550 success
= (sync_error
== NULL
);
552 CFRetainAssign(error
, sync_error
);
557 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", error
);
560 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
563 CFReleaseNull(deviceID
);
566 require_action_quiet(account
->trusted_circle
, xit
,
567 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Account trusted circle not set"), NULL
, &error
));
569 require_action_quiet(hasID
, xit
,
570 SOSCreateError(kSOSErrorBadFormat
, CFSTR("Missing IDS device ID"), NULL
, &error
));
571 ok
= SOSCircleHasPeerWithID(account
->trusted_circle
,
572 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
)), &error
);
575 secerror("sync with device failure: %@", error
);
577 CFReleaseSafe(error
);
581 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account
, CFStringRef circleName
, CFStringRef peerID
) {
582 SOSPeerInfoRef mypi
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
583 CFStringRef myPeerID
= SOSPeerInfoGetPeerID(mypi
);
585 return myPeerID
&& CFEqualSafe(myPeerID
, peerID
);
588 static bool isDefaultsWriteSetupToSyncOverIDS(){
589 return ((whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
));
592 bool SOSAccountSyncWithAllPeers(SOSAccountRef account
, CFErrorRef
*error
)
595 __block
bool SyncingCompletedOverIDS
= true;
596 __block
bool SyncingCompletedOverKVS
= true;
597 __block CFErrorRef localError
= NULL
;
598 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
599 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
600 CFMutableArrayRef peerIds
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
602 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account
), xit
,
603 SOSCreateError(kSOSErrorNoCircle
, CFSTR("This device cannot sync with circle"),
606 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
607 if (!SOSAccountIsThisPeerIDMe(account
, SOSCircleGetName(circle
), SOSPeerInfoGetPeerID(peer
))) {
608 if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), peer
)) {
609 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
610 CFMutableDictionaryRef circleToIdsId
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, SOSCircleGetName(circle
), SOSPeerInfoGetPeerID((peer
)), NULL
);
611 SyncingCompletedOverIDS
= SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, circleToIdsId
, &localError
);
612 CFReleaseNull(circleToIdsId
);
614 CFArrayAppendValue(peerIds
, SOSPeerInfoGetPeerID(peer
));
618 if (CFArrayGetCount(peerIds
)) {
619 secnotice("KVS", "Syncing with KVS capable peers");
620 CFDictionarySetValue(circleToPeerIDs
, SOSCircleGetName(circle
), peerIds
);
621 SyncingCompletedOverKVS
&= SOSTransportMessageSyncWithPeers(account
->kvs_message_transport
, circleToPeerIDs
, &localError
);
624 SOSEngineRef engine
= SOSTransportMessageGetEngine(account
->kvs_message_transport
);
625 result
= SOSEngineSyncWithPeers(engine
, account
->ids_message_transport
, account
->kvs_message_transport
, &localError
);
627 result
&= ((SyncingCompletedOverIDS
) &&
628 (SyncingCompletedOverKVS
|| (CFDictionaryGetCount(circleToPeerIDs
) == 0)));
631 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers
, 1);
634 CFReleaseNull(circleToPeerIDs
);
637 secdebug("Account", "Could not sync with all peers: %@", localError
);
638 CFErrorPropagate(localError
, error
);
641 CFReleaseNull(peerIds
);
642 CFReleaseSafe(localError
);
646 bool SOSAccountCleanupAfterPeer(SOSAccountRef account
, size_t seconds
, SOSCircleRef circle
,
647 SOSPeerInfoRef cleanupPeer
, CFErrorRef
* error
)
651 SOSPeerInfoRef myPeerInfo
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
652 require_action_quiet(account
->my_identity
&& myPeerInfo
, xit
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("I have no peer")));
653 require_quiet(SOSCircleHasActivePeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), error
), xit
);
655 CFStringRef cleanupPeerID
= SOSPeerInfoGetPeerID(cleanupPeer
);
657 CFStringRef circle_name
= SOSCircleGetName(circle
);
659 CFMutableDictionaryRef circleToPeerIDs
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
660 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs
, circle_name
), cleanupPeerID
);
662 CFErrorRef localError
= NULL
;
663 if (!(success
&= SOSTransportMessageCleanupAfterPeerMessages(account
->kvs_message_transport
, circleToPeerIDs
, &localError
))) {
664 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
667 if (account
->ids_message_transport
&& !SOSTransportMessageCleanupAfterPeerMessages(account
->ids_message_transport
, circleToPeerIDs
, &localError
)) {
668 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID
, localError
);
671 CFReleaseNull(localError
);
673 if((success
&= SOSPeerInfoRetireRetirementTicket(seconds
, cleanupPeer
))) {
674 if (!(success
&= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, circleToPeerIDs
, &localError
))) {
675 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID
, localError
);
678 CFReleaseNull(localError
);
679 CFReleaseNull(circleToPeerIDs
);
685 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account
, size_t seconds
, CFErrorRef
* error
) {
686 CFMutableSetRef retirees_to_remove
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
688 __block
bool success
= true;
690 CFSetForEach(account
->retirees
, ^(const void *value
) {
691 SOSPeerInfoRef retiree
= (SOSPeerInfoRef
) value
;
694 // 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.
695 if (!SOSPeerInfoIsRetirementTicket(retiree
) ||
696 (SOSPeerInfoRetireRetirementTicket(seconds
, retiree
) && !SOSCircleHasActivePeer(account
->trusted_circle
, retiree
, NULL
))) {
697 CFSetAddValue(retirees_to_remove
, retiree
);
702 CFMutableArrayRef retirees_to_cleanup
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
703 CFSetForEach(retirees_to_remove
, ^(const void *value
) {
704 CFArrayAppendValue(retirees_to_cleanup
, value
);
705 CFSetRemoveValue(account
->retirees
, value
);
708 CFReleaseNull(retirees_to_remove
);
710 CFDictionaryRef retirements_to_remove
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
711 SOSCircleGetName(account
->trusted_circle
), retirees_to_cleanup
,
714 CFReleaseNull(retirees_to_cleanup
);
716 success
= SOSTransportCircleExpireRetirementRecords(account
->circle_transport
, retirements_to_remove
, error
);
718 CFReleaseNull(retirements_to_remove
);
723 bool SOSAccountScanForRetired(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
*error
) {
724 SOSCircleForEachRetiredPeer(circle
, ^(SOSPeerInfoRef peer
) {
725 CFSetSetValue(account
->retirees
, peer
);
726 CFErrorRef cleanupError
= NULL
;
727 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, peer
, &cleanupError
)) {
728 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError
);
730 CFReleaseSafe(cleanupError
);
735 SOSCircleRef
SOSAccountCloneCircleWithRetirement(SOSAccountRef account
, SOSCircleRef starting_circle
, CFErrorRef
*error
) {
736 SOSCircleRef new_circle
= SOSCircleCopyCircle(NULL
, starting_circle
, error
);
737 if(!new_circle
) return NULL
;
739 if (account
->retirees
) {
740 CFSetForEach(account
->retirees
, ^(const void* value
) {
741 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) value
;
742 if (isSOSPeerInfo(pi
)) {
743 SOSCircleUpdatePeerInfo(new_circle
, pi
);
748 if(SOSCircleCountPeers(new_circle
) == 0) {
749 SOSCircleResetToEmpty(new_circle
, NULL
);
756 // MARK: Circle Membership change notificaion
759 void SOSAccountAddChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
760 SOSAccountCircleMembershipChangeBlock copy
= Block_copy(changeBlock
);
761 CFArrayAppendValue(a
->change_blocks
, copy
);
765 void SOSAccountRemoveChangeBlock(SOSAccountRef a
, SOSAccountCircleMembershipChangeBlock changeBlock
) {
766 CFArrayRemoveAllValue(a
->change_blocks
, changeBlock
);
769 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a
, CFStringRef ds_name
, SOSAccountSyncablePeersBlock changeBlock
) {
770 if (!changeBlock
) return;
772 CFRetainSafe(ds_name
);
773 SOSAccountCircleMembershipChangeBlock block_to_register
= ^void (SOSCircleRef new_circle
,
774 CFSetRef added_peers
, CFSetRef removed_peers
,
775 CFSetRef added_applicants
, CFSetRef removed_applicants
) {
777 if (!CFEqualSafe(SOSCircleGetName(new_circle
), ds_name
))
780 SOSPeerInfoRef myPi
= SOSFullPeerInfoGetPeerInfo(a
->my_identity
);
781 CFStringRef myPi_id
= myPi
? SOSPeerInfoGetPeerID(myPi
) : NULL
;
783 CFMutableArrayRef peer_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
784 CFMutableArrayRef added_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
785 CFMutableArrayRef removed_ids
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
787 if (SOSCircleHasPeer(new_circle
, myPi
, NULL
)) {
788 SOSCircleForEachPeer(new_circle
, ^(SOSPeerInfoRef peer
) {
789 CFArrayAppendValueIfNot(peer_ids
, SOSPeerInfoGetPeerID(peer
), myPi_id
);
792 CFSetForEach(added_peers
, ^(const void *value
) {
793 CFArrayAppendValueIfNot(added_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
796 CFSetForEach(removed_peers
, ^(const void *value
) {
797 CFArrayAppendValueIfNot(removed_ids
, SOSPeerInfoGetPeerID((SOSPeerInfoRef
) value
), myPi_id
);
801 if (CFArrayGetCount(peer_ids
) || CFSetContainsValue(removed_peers
, myPi
))
802 changeBlock(peer_ids
, added_ids
, removed_ids
);
804 CFReleaseSafe(peer_ids
);
805 CFReleaseSafe(added_ids
);
806 CFReleaseSafe(removed_ids
);
809 CFRetainSafe(changeBlock
);
810 SOSAccountAddChangeBlock(a
, block_to_register
);
812 CFSetRef empty
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
813 if (a
->trusted_circle
&& CFEqualSafe(ds_name
, SOSCircleGetName(a
->trusted_circle
))) {
814 block_to_register(a
->trusted_circle
, empty
, empty
, empty
, empty
);
816 CFReleaseSafe(empty
);
819 void SOSAccountPurgeIdentity(SOSAccountRef account
) {
820 if (account
->my_identity
) {
821 // Purge private key but don't return error if we can't.
822 CFErrorRef purgeError
= NULL
;
823 if (!SOSFullPeerInfoPurgePersistentKey(account
->my_identity
, &purgeError
)) {
824 secwarning("Couldn't purge persistent key for %@ [%@]", account
->my_identity
, purgeError
);
826 CFReleaseNull(purgeError
);
828 CFReleaseNull(account
->my_identity
);
832 bool sosAccountLeaveCircle(SOSAccountRef account
, SOSCircleRef circle
, CFErrorRef
* error
) {
833 SOSFullPeerInfoRef fpi
= account
->my_identity
;
834 if(!fpi
) return false;
836 CFErrorRef localError
= NULL
;
840 SOSPeerInfoRef retire_peer
= SOSFullPeerInfoPromoteToRetiredAndCopy(fpi
, &localError
);
842 secerror("Create ticket failed for peer %@: %@", fpi
, localError
);
844 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
845 if(SOSCircleHasApplicant(circle
, retire_peer
, NULL
)) {
846 // Remove our application if we have one.
847 SOSCircleWithdrawRequest(circle
, retire_peer
, NULL
);
848 } else if (SOSCircleHasPeer(circle
, retire_peer
, NULL
)) {
849 if (SOSCircleUpdatePeerInfo(circle
, retire_peer
)) {
850 CFErrorRef cleanupError
= NULL
;
851 if (!SOSAccountCleanupAfterPeer(account
, RETIREMENT_FINALIZATION_SECONDS
, circle
, retire_peer
, &cleanupError
)) {
852 secerror("Error cleanup up after peer (%@): %@", retire_peer
, cleanupError
);
854 CFReleaseSafe(cleanupError
);
858 // Store the retirement record locally.
859 CFSetAddValue(account
->retirees
, retire_peer
);
861 // Write retirement to Transport
862 CFErrorRef postError
= NULL
;
863 if (!SOSTransportCirclePostRetirement(account
->circle_transport
, SOSCircleGetName(circle
), retire_peer
, &postError
)){
864 secwarning("Couldn't post retirement (%@)", postError
);
866 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &postError
)){
867 secwarning("Couldn't flush retirement data (%@)", postError
);
869 CFReleaseNull(postError
);
872 SOSAccountPurgeIdentity(account
);
876 CFReleaseNull(localError
);
877 CFReleaseNull(retire_peer
);
881 bool sosAccountLeaveRing(SOSAccountRef account
, SOSRingRef ring
, CFErrorRef
* error
) {
882 SOSFullPeerInfoRef fpi
= account
->my_identity
;
883 if(!fpi
) return false;
884 SOSPeerInfoRef pi
= SOSFullPeerInfoGetPeerInfo(fpi
);
885 CFStringRef peerID
= SOSPeerInfoGetPeerID(pi
);
887 CFErrorRef localError
= NULL
;
890 bool writeRing
= false;
891 bool writePeerInfo
= false;
893 if(SOSRingHasPeerID(ring
, peerID
)) {
894 writePeerInfo
= true;
898 // this was circle behavior - at some point
899 if(SOSRingHasApplicant(ring
, peerID
)) {
904 if(writePeerInfo
|| writeRing
) {
905 SOSRingWithdraw(ring
, NULL
, fpi
, error
);
908 // Write leave thing to Transport
909 CFDataRef peerInfoData
= SOSFullPeerInfoCopyEncodedData(fpi
, kCFAllocatorDefault
, error
);
910 SOSTransportCircleSendPeerInfo(account
->circle_transport
, peerID
, peerInfoData
, NULL
); // TODO: Handle errors?
913 CFDataRef ring_data
= SOSRingCopyEncodedData(ring
, error
);
916 SOSTransportCircleRingPostRing(account
->circle_transport
, SOSRingGetName(ring
), ring_data
, NULL
); // TODO: Handle errors?
918 CFReleaseNull(ring_data
);
921 CFReleaseNull(localError
);
925 bool SOSAccountPostDebugScope(SOSAccountRef account
, CFTypeRef scope
, CFErrorRef
*error
) {
927 SOSTransportCircleRef transport
= account
->circle_transport
;
929 result
= SOSTransportCircleSendDebugInfo(transport
, kSOSAccountDebugScope
, scope
, error
);
935 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
936 local value that has been overwritten by a distant value. If there is no
937 conflict between the local and the distant values when doing the initial
938 sync (e.g. if the cloud has no data stored or the client has not stored
939 any data yet), you'll never see that notification.
941 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
942 with server but initial round trip with server does not imply
943 NSUbiquitousKeyValueStoreInitialSyncChange.
948 // MARK: Status summary
951 static SOSCCStatus
SOSCCThisDeviceStatusInCircle(SOSCircleRef circle
, SOSPeerInfoRef this_peer
) {
953 return kSOSCCNotInCircle
;
955 if (circle
&& SOSCircleCountPeers(circle
) == 0)
956 return kSOSCCCircleAbsent
;
960 if(SOSPeerInfoIsRetirementTicket(this_peer
))
961 return kSOSCCNotInCircle
;
963 if (SOSCircleHasPeer(circle
, this_peer
, NULL
))
964 return kSOSCCInCircle
;
966 if (SOSCircleHasApplicant(circle
, this_peer
, NULL
))
967 return kSOSCCRequestPending
;
970 return kSOSCCNotInCircle
;
973 bool SOSAccountIsInCircle(SOSAccountRef account
, CFErrorRef
*error
) {
974 SOSCCStatus result
= SOSAccountGetCircleStatus(account
, error
);
976 if (result
!= kSOSCCInCircle
&& result
!= kSOSCCError
) {
977 SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("Not in circle"));
984 SOSCCStatus
SOSAccountGetCircleStatus(SOSAccountRef account
, CFErrorRef
* error
) {
985 if (!SOSAccountHasPublicKey(account
, error
)) {
989 return SOSCCThisDeviceStatusInCircle(account
->trusted_circle
, SOSAccountGetMyPeerInfo(account
));
993 // MARK: Account Reset Circles
996 static bool SOSAccountResetCircleToOffering(SOSAccountRef account
, SecKeyRef user_key
, CFErrorRef
*error
) {
999 require(SOSAccountHasCircle(account
, error
), fail
);
1000 require(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1002 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1003 bool result
= false;
1004 SOSFullPeerInfoRef cloud_identity
= NULL
;
1005 CFErrorRef localError
= NULL
;
1007 require_quiet(SOSCircleResetToOffering(circle
, user_key
, account
->my_identity
, &localError
), err_out
);
1010 SOSPeerInfoRef cloud_peer
= GenerateNewCloudIdentityPeerInfo(error
);
1011 require_quiet(cloud_peer
, err_out
);
1012 cloud_identity
= CopyCloudKeychainIdentity(cloud_peer
, error
);
1013 CFReleaseNull(cloud_peer
);
1014 require_quiet(cloud_identity
, err_out
);
1017 account
->departure_code
= kSOSNeverLeftCircle
;
1018 require_quiet(SOSCircleRequestAdmission(circle
, user_key
, cloud_identity
, &localError
), err_out
);
1019 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, account
->my_identity
, SOSFullPeerInfoGetPeerInfo(cloud_identity
), &localError
), err_out
);
1021 SOSAccountPublishCloudParameters(account
, NULL
);
1024 if (result
== false)
1025 secerror("error resetting circle (%@) to offering: %@", circle
, localError
);
1026 if (localError
&& error
&& *error
== NULL
) {
1027 *error
= localError
;
1030 CFReleaseNull(localError
);
1031 CFReleaseNull(cloud_identity
);
1042 bool SOSAccountResetToOffering(SOSAccountRef account
, CFErrorRef
* error
) {
1043 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1047 CFReleaseNull(account
->my_identity
);
1049 return user_key
&& SOSAccountResetCircleToOffering(account
, user_key
, error
);
1052 bool SOSAccountResetToEmpty(SOSAccountRef account
, CFErrorRef
* error
) {
1053 if (!SOSAccountHasPublicKey(account
, error
))
1056 CFReleaseNull(account
->my_identity
);
1058 __block
bool result
= true;
1059 account
->departure_code
= kSOSWithdrewMembership
;
1060 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1061 result
= SOSCircleResetToEmpty(circle
, error
);
1066 secerror("error: %@", error
? *error
: NULL
);
1074 // MARK: Waiting for in-sync
1077 static bool SOSAccountHasBeenInSync(SOSAccountRef account
) {
1078 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1079 CFSetRef unsynced
= asSet(unsyncedObject
, NULL
);
1081 return !(unsyncedObject
== kCFBooleanTrue
|| (unsynced
&& (CFSetGetCount(unsynced
) > 0)));
1084 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account
, CFSetRef viewsInSync
) {
1085 bool notifyOfChange
= false;
1087 SOSCCStatus circleStatus
= SOSAccountGetCircleStatus(account
, NULL
);
1088 bool inOrApplying
= (circleStatus
== kSOSCCInCircle
) || (circleStatus
== kSOSCCRequestPending
);
1090 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1092 if (!inOrApplying
) {
1093 if (unsyncedObject
!= NULL
) {
1094 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1095 secnotice("initial-sync", "in sync, clearing pending");
1096 notifyOfChange
= true;
1098 } else if (circleStatus
== kSOSCCInCircle
) {
1099 __block CFMutableSetRef viewsToSync
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
1100 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1101 SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) {
1102 CFSetUnion(viewsToSync
, enabled
);
1107 CFSetSubtract(viewsToSync
, viewsInSync
);
1111 if (unsyncedObject
== kCFBooleanTrue
) {
1112 if (CFSetGetCount(viewsToSync
) == 0) {
1113 secnotice("initial-sync", "No views to wait for");
1114 SOSAccountClearValue(account
, kSOSUnsyncedViewsKey
, NULL
);
1116 __block CFSetRef newViews
= NULL
;
1117 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account
), ^(CFSetRef enabled
) {
1118 newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, enabled
, viewsToSync
);
1120 secnotice("initial-sync", "Pending views set from True: %@", newViews
);
1121 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1122 CFReleaseNull(newViews
);
1124 notifyOfChange
= true;
1125 } else if (isSet(unsyncedObject
)) {
1126 CFSetRef waiting
= (CFMutableSetRef
) unsyncedObject
;
1127 CFSetRef newViews
= CFSetCreateIntersection(kCFAllocatorDefault
, waiting
, viewsToSync
);
1128 if (!CFEqualSafe(waiting
, newViews
)) {
1129 secnotice("initial-sync", "Pending views updated: %@", newViews
);
1130 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newViews
, NULL
);
1131 notifyOfChange
= true;
1133 CFReleaseNull(newViews
);
1136 CFReleaseNull(viewsToSync
);
1139 if (notifyOfChange
) {
1140 secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification
);
1141 notify_post(kSOSCCInitialSyncChangedNotification
);
1142 // Make sure we update the engine
1143 account
->circle_rings_retirements_need_attention
= true;
1146 return SOSAccountHasBeenInSync(account
);
1149 static void SOSAccountPeerGotInSync(SOSAccountRef account
, CFStringRef peerID
) {
1150 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID
);
1152 if (account
->trusted_circle
) {
1153 SOSPeerInfoRef peer
= SOSCircleCopyPeerWithID(account
->trusted_circle
, peerID
, NULL
);
1155 CFSetRef views
= SOSPeerInfoCopyEnabledViews(peer
);
1156 SOSAccountUpdateOutOfSyncViews(account
, views
);
1157 CFReleaseNull(views
);
1159 CFReleaseNull(peer
);
1163 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account
) {
1164 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1166 CFDictionaryForEach(account
->notification_cleanups
, ^(const void *key
, const void *value
) {
1168 SOSEngineSetSyncCompleteListener(engine
, key
, NULL
);
1170 dispatch_async(account
->queue
, value
);
1173 CFDictionaryRemoveAllValues(account
->notification_cleanups
);
1176 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account
, CFStringRef peerID
) {
1177 dispatch_block_t cleanup
= CFDictionaryGetValue(account
->notification_cleanups
, peerID
);
1180 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1183 SOSEngineSetSyncCompleteListener(engine
, peerID
, NULL
);
1186 dispatch_async(account
->queue
, cleanup
);
1189 CFDictionaryRemoveValue(account
->notification_cleanups
, peerID
);
1193 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account
, CFStringRef peerID
, dispatch_block_t block
) {
1194 dispatch_block_t copy
= Block_copy(block
);
1195 CFDictionarySetValue(account
->notification_cleanups
, peerID
, copy
);
1196 CFReleaseNull(copy
);
1199 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) {
1200 if (CFDictionaryGetCount(account
->notification_cleanups
) == 0) {
1201 SOSEngineRef engine
= SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
1203 SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
);
1206 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
1207 CFStringRef peerID
= CFStringCreateCopy(kCFAllocatorDefault
, SOSPeerInfoGetPeerID(peer
));
1209 SOSAccountRegisterCleanupBlock(account
, peerID
, ^{
1210 CFReleaseSafe(peerID
);
1213 SOSEngineSetSyncCompleteListener(engine
, peerID
, ^{
1214 SOSAccountPeerGotInSync(account
, peerID
);
1215 SOSAccountCleanupNotificationForPeer(account
, peerID
);
1216 SOSAccountFinishTransaction(account
);
1220 secerror("Couldn't find engine to setup notifications!!!");
1225 void SOSAccountCancelSyncChecking(SOSAccountRef account
) {
1226 SOSAccountCleanupNotificationForAllPeers(account
);
1227 SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1230 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account
) {
1231 bool hasBeenInSync
= false;
1233 if (!SOSAccountIsInCircle(account
, NULL
)) {
1234 SOSAccountCancelSyncChecking(account
);
1236 hasBeenInSync
= SOSAccountHasBeenInSync(account
);
1237 if (!hasBeenInSync
) {
1238 hasBeenInSync
= SOSAccountUpdateOutOfSyncViews(account
, NULL
);
1239 if (hasBeenInSync
) {
1240 // Cancel and declare victory
1241 SOSAccountCancelSyncChecking(account
);
1243 // Make sure we're watching in case this is the fist attempt
1244 SOSAccountEnsureSyncChecking(account
);
1249 return hasBeenInSync
;
1256 static bool SOSAccountJoinCircle(SOSAccountRef account
, SecKeyRef user_key
,
1257 bool use_cloud_peer
, CFErrorRef
* error
) {
1258 __block
bool result
= false;
1259 __block SOSFullPeerInfoRef cloud_full_peer
= NULL
;
1261 require_action_quiet(account
->trusted_circle
, fail
, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound
, NULL
, error
, NULL
, CFSTR("Don't have circle when joining???")));
1262 require_quiet(SOSAccountEnsureFullPeerAvailable(account
, error
), fail
);
1264 SOSFullPeerInfoRef myCirclePeer
= account
->my_identity
;
1266 if (use_cloud_peer
) {
1267 cloud_full_peer
= SOSCircleCopyiCloudFullPeerInfoRef(account
->trusted_circle
, NULL
);
1269 SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, kCFBooleanTrue
, NULL
);
1272 if (SOSCircleCountPeers(account
->trusted_circle
) == 0) {
1273 result
= SOSAccountResetCircleToOffering(account
, user_key
, error
);
1275 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1276 result
= SOSCircleRequestAdmission(circle
, user_key
, myCirclePeer
, error
);
1277 account
->departure_code
= kSOSNeverLeftCircle
;
1278 if(result
&& cloud_full_peer
) {
1279 CFErrorRef localError
= NULL
;
1280 CFStringRef cloudid
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer
));
1281 require_quiet(cloudid
, finish
);
1282 require_quiet(SOSCircleHasActivePeerWithID(circle
, cloudid
, &localError
), finish
);
1283 require_quiet(SOSCircleAcceptRequest(circle
, user_key
, cloud_full_peer
, SOSFullPeerInfoGetPeerInfo(myCirclePeer
), &localError
), finish
);
1286 secerror("Failed to join with cloud identity: %@", localError
);
1287 CFReleaseNull(localError
);
1295 CFReleaseNull(cloud_full_peer
);
1299 static bool SOSAccountJoinCircles_internal(SOSAccountRef account
, bool use_cloud_identity
, CFErrorRef
* error
) {
1300 bool success
= false;
1302 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1303 require_quiet(user_key
, done
); // Fail if we don't get one.
1305 require_action_quiet(account
->trusted_circle
, done
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle to join")));
1307 if (account
->my_identity
!= NULL
) {
1308 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1309 success
= SOSCircleHasPeer(account
->trusted_circle
, myPeer
, NULL
);
1310 require_quiet(!success
, done
);
1312 SOSCircleRemoveRejectedPeer(account
->trusted_circle
, myPeer
, NULL
); // If we were rejected we should remove it now.
1314 if (!SOSCircleHasApplicant(account
->trusted_circle
, myPeer
, NULL
)) {
1315 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer
), SOSCircleGetName(account
->trusted_circle
));
1317 CFReleaseNull(account
->my_identity
);
1322 success
= SOSAccountJoinCircle(account
, user_key
, use_cloud_identity
, error
);
1324 require_quiet(success
, done
);
1326 account
->departure_code
= kSOSNeverLeftCircle
;
1332 bool SOSAccountJoinCircles(SOSAccountRef account
, CFErrorRef
* error
) {
1333 return SOSAccountJoinCircles_internal(account
, false, error
);
1336 CFStringRef
SOSAccountCopyDeviceID(SOSAccountRef account
, CFErrorRef
*error
){
1337 CFStringRef result
= NULL
;
1339 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1341 result
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1347 bool SOSAccountSetMyDSID(SOSAccountRef account
, CFStringRef IDS
, CFErrorRef
* error
){
1350 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
){
1351 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1352 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1353 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1355 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1357 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1358 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeIDS
, error
);
1359 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1361 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1367 else if(whichTransportType
== kSOSTransportPresent
){
1368 secdebug("IDS Transport", "We are setting our device ID: %@", IDS
);
1369 if(IDS
!= NULL
&& (CFStringGetLength(IDS
) > 0)){
1370 require_action_quiet(account
->my_identity
, fail
, SOSErrorCreate(kSOSErrorPeerNotFound
, error
, NULL
, CFSTR("No peer for me")));
1372 result
= SOSAccountModifyCircle(account
, error
, ^bool(SOSCircleRef circle
) {
1374 SOSFullPeerInfoUpdateDeviceID(account
->my_identity
, IDS
, error
);
1375 SOSFullPeerInfoUpdateTransportType(account
->my_identity
, SOSTransportMessageTypeKVS
, error
);
1376 SOSFullPeerInfoUpdateTransportPreference(account
->my_identity
, kCFBooleanTrue
, error
);
1378 return SOSCircleHasPeer(circle
, SOSFullPeerInfoGetPeerInfo(account
->my_identity
), NULL
);
1386 SOSCCSyncWithAllPeers();
1393 bool SOSAccountSendIDSTestMessage(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1395 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1396 //construct message dictionary, circle -> peerID -> message
1398 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1399 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1401 CFStringRef operation
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSSendOneMessage
);
1402 CFDataRef operationData
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, operation
, kCFStringEncodingUTF8
, 0);
1404 CFDataRef messageData
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
, 0);
1405 CFMutableDataRef mutableData
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(operationData
) + CFDataGetLength(messageData
));
1407 CFDataAppend(mutableData
, operationData
);
1408 CFDataAppend(mutableData
, messageData
);
1410 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1411 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableData
);
1414 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1415 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1417 CFReleaseNull(operation
);
1418 CFReleaseNull(mutableData
);
1419 CFReleaseNull(operationData
);
1420 CFReleaseNull(messageData
);
1421 CFReleaseNull(peerToMessage
);
1422 CFReleaseNull(circleToPeerMessages
);
1427 bool SOSAccountStartPingTest(SOSAccountRef account
, CFStringRef message
, CFErrorRef
*error
){
1429 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1430 //construct message dictionary, circle -> peerID -> message
1432 CFMutableDictionaryRef circleToPeerMessages
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1433 CFMutableDictionaryRef peerToMessage
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1435 CFStringRef operation
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSStartPingTestMessage
);
1436 CFDataRef operationData
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, operation
, kCFStringEncodingUTF8
, 0);
1438 CFDataRef messageData
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, message
, kCFStringEncodingUTF8
, 0);
1439 CFMutableDataRef mutableData
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(operationData
) + CFDataGetLength(messageData
));
1441 CFDataAppend(mutableData
, operationData
);
1442 CFDataAppend(mutableData
, messageData
);
1444 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1445 CFDictionaryAddValue(peerToMessage
, SOSPeerInfoGetPeerID(peer
), mutableData
);
1448 CFDictionaryAddValue(circleToPeerMessages
, SOSCircleGetName(account
->trusted_circle
), peerToMessage
);
1449 result
= SOSTransportMessageSendMessages(account
->ids_message_transport
, circleToPeerMessages
, error
);
1451 CFReleaseNull(operation
);
1452 CFReleaseNull(mutableData
);
1453 CFReleaseNull(operationData
);
1454 CFReleaseNull(messageData
);
1455 CFReleaseNull(peerToMessage
);
1456 CFReleaseNull(circleToPeerMessages
);
1461 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account
, CFErrorRef
*error
){
1463 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1465 __block
bool success
= false;
1467 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
1468 dispatch_retain(wait_for
); // Both this scope and the block own it
1470 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1471 success
= (sync_error
== NULL
);
1473 CFRetainAssign(*error
, sync_error
);
1476 dispatch_semaphore_signal(wait_for
);
1477 dispatch_release(wait_for
);
1480 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
1481 dispatch_release(wait_for
);
1484 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", *error
);
1487 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1493 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account
, CFErrorRef
* error
) {
1494 return SOSAccountJoinCircles_internal(account
, true, error
);
1498 bool SOSAccountLeaveCircle(SOSAccountRef account
, CFErrorRef
* error
)
1502 result
&= SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1503 return sosAccountLeaveCircle(account
, circle
, error
);
1506 account
->departure_code
= kSOSWithdrewMembership
;
1511 bool SOSAccountBail(SOSAccountRef account
, uint64_t limit_in_seconds
, CFErrorRef
* error
) {
1512 dispatch_queue_t queue
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
1513 dispatch_group_t group
= dispatch_group_create();
1514 __block
bool result
= false;
1515 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds
);
1516 // Add a task to the group
1517 dispatch_group_async(group
, queue
, ^{
1518 SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle
) {
1519 return sosAccountLeaveCircle(account
, circle
, error
);
1522 dispatch_time_t milestone
= dispatch_time(DISPATCH_TIME_NOW
, limit_in_seconds
* NSEC_PER_SEC
);
1523 dispatch_group_wait(group
, milestone
);
1525 account
->departure_code
= kSOSWithdrewMembership
;
1527 dispatch_release(group
);
1533 // MARK: Application
1536 static void for_each_applicant_in_each_circle(SOSAccountRef account
, CFArrayRef peer_infos
,
1537 bool (^action
)(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
)) {
1538 SOSPeerInfoRef me
= SOSFullPeerInfoGetPeerInfo(account
->my_identity
);
1539 CFErrorRef peer_error
= NULL
;
1540 if (account
->trusted_circle
&& me
&&
1541 SOSCircleHasPeer(account
->trusted_circle
, me
, &peer_error
)) {
1542 SOSAccountModifyCircle(account
, NULL
, ^(SOSCircleRef circle
) {
1543 __block
bool modified
= false;
1544 CFArrayForEach(peer_infos
, ^(const void *value
) {
1545 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) value
;
1546 if (isSOSPeerInfo(peer
) && SOSCircleHasApplicant(circle
, peer
, NULL
)) {
1547 if (action(circle
, account
->my_identity
, peer
)) {
1556 secerror("Got error in SOSCircleHasPeer: %@", peer_error
);
1557 CFReleaseSafe(peer_error
); // TODO: We should be accumulating errors here.
1560 bool SOSAccountAcceptApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1561 SecKeyRef user_key
= SOSAccountGetPrivateCredential(account
, error
);
1565 __block
bool success
= true;
1566 __block
int64_t num_peers
= 0;
1568 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1569 bool accepted
= SOSCircleAcceptRequest(circle
, user_key
, myCirclePeer
, peer
, error
);
1573 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1580 bool SOSAccountRejectApplicants(SOSAccountRef account
, CFArrayRef applicants
, CFErrorRef
* error
) {
1581 __block
bool success
= true;
1582 __block
int64_t num_peers
= 0;
1584 for_each_applicant_in_each_circle(account
, applicants
, ^(SOSCircleRef circle
, SOSFullPeerInfoRef myCirclePeer
, SOSPeerInfoRef peer
) {
1585 bool rejected
= SOSCircleRejectRequest(circle
, myCirclePeer
, peer
, error
);
1589 num_peers
= MAX(num_peers
, SOSCircleCountPeers(circle
));
1597 CFStringRef
SOSAccountCopyIncompatibilityInfo(SOSAccountRef account
, CFErrorRef
* error
) {
1598 return CFSTR("We're compatible, go away");
1601 enum DepartureReason
SOSAccountGetLastDepartureReason(SOSAccountRef account
, CFErrorRef
* error
) {
1602 return account
->departure_code
;
1605 void SOSAccountSetLastDepartureReason(SOSAccountRef account
, enum DepartureReason reason
) {
1606 account
->departure_code
= reason
;
1610 CFArrayRef
SOSAccountCopyGeneration(SOSAccountRef account
, CFErrorRef
*error
) {
1611 CFArrayRef result
= NULL
;
1612 CFNumberRef generation
= NULL
;
1614 require_quiet(SOSAccountHasPublicKey(account
, error
), fail
);
1615 require_action_quiet(account
->trusted_circle
, fail
, SOSErrorCreate(kSOSErrorNoCircle
, error
, NULL
, CFSTR("No circle")));
1617 generation
= (CFNumberRef
)SOSCircleGetGeneration(account
->trusted_circle
);
1618 result
= CFArrayCreateForCFTypes(kCFAllocatorDefault
, generation
, NULL
);
1624 bool SOSValidateUserPublic(SOSAccountRef account
, CFErrorRef
*error
) {
1625 if (!SOSAccountHasPublicKey(account
, error
))
1628 return account
->user_public_trusted
;
1631 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account
, CFErrorRef
*error
) {
1632 // TODO: this result is never set or used
1635 secnotice("updates", "Ensuring peer registration.");
1637 require_quiet(account
->trusted_circle
, done
);
1638 require_quiet(account
->my_identity
, done
);
1639 // If we are not in the circle, there is no point in setting up peers
1640 require_quiet(SOSAccountIsMyPeerActive(account
, NULL
), done
);
1642 // This code only uses the SOSFullPeerInfoRef for two things:
1643 // - Finding out if this device is in the trusted circle
1644 // - Using the peerID for this device to see if the current peer is "me"
1645 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1647 CFStringRef my_id
= SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1649 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
1650 if (!SOSPeerInfoPeerIDEqual(peer
, my_id
)) {
1651 CFErrorRef localError
= NULL
;
1652 SOSTransportMessageRef messageTransport
= NULL
;
1654 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1655 messageTransport
= SOSPeerInfoHasDeviceID(peer
) ? account
->ids_message_transport
: account
->kvs_message_transport
;
1658 messageTransport
= account
->kvs_message_transport
;
1660 SOSPeerCoderInitializeForPeer(messageTransport
->engine
, account
->my_identity
, peer
, &localError
);
1662 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer
, account
->my_identity
, localError
);
1663 CFReleaseSafe(localError
);
1667 //Initialize our device ID
1668 if(whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
){
1669 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
1670 if( deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
1672 __block
bool success
= false;
1673 __block CFErrorRef localError
= NULL
;
1675 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
1676 success
= (sync_error
== NULL
);
1678 CFRetainAssign(localError
, sync_error
);
1683 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError
);
1686 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1688 CFReleaseNull(localError
);
1690 CFReleaseNull(deviceID
);
1697 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account
, CFErrorRef
*error
) {
1698 if (!account
->expansion
) {
1699 account
->expansion
= CFDictionaryCreateMutableForCFTypes(NULL
);
1702 return SecAllocationError(account
->expansion
, error
, CFSTR("Can't Alloc Account Expansion dictionary"));
1705 bool SOSAccountClearValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1706 bool success
= SOSAccountEnsureExpansion(account
, error
);
1707 require_quiet(success
, errOut
);
1709 CFDictionaryRemoveValue(account
->expansion
, key
);
1714 bool SOSAccountSetValue(SOSAccountRef account
, const void *key
, const void *value
, CFErrorRef
*error
) {
1715 bool success
= SOSAccountEnsureExpansion(account
, error
);
1716 require_quiet(success
, errOut
);
1718 CFDictionarySetValue(account
->expansion
, key
, value
);
1724 const void *SOSAccountGetValue(SOSAccountRef account
, const void *key
, CFErrorRef
*error
) {
1725 if (!account
->expansion
) {
1728 return CFDictionaryGetValue(account
->expansion
, key
);
1731 void SOSAccountFinishTransaction(SOSAccountRef account
) {
1732 if(account
->circle_rings_retirements_need_attention
){
1733 CFErrorRef localError
= NULL
;
1734 if(!SOSTransportCircleFlushChanges(account
->circle_transport
, &localError
)) {
1735 secerror("flush circle failed %@", localError
);
1737 CFReleaseSafe(localError
);
1739 SOSAccountNotifyEngines(account
); // For now our only rings are backup rings.
1742 SOSAccountCheckHasBeenInSync(account
);
1744 account
->circle_rings_retirements_need_attention
= false;