]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ProjectHeaders/Security/SecureObjectSync/SOSAccount.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / ProjectHeaders / Security / SecureObjectSync / SOSAccount.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5 /*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10 #include "SOSAccountPriv.h"
11 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
12 #include <Security/SecureObjectSync/SOSTransportCircle.h>
13 #include <Security/SecureObjectSync/SOSTransportMessage.h>
14 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
15 #include <Security/SecureObjectSync/SOSKVSKeys.h>
16 #include <Security/SecureObjectSync/SOSTransport.h>
17 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
18 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
19 #include <Security/SecureObjectSync/SOSEngine.h>
20 #include <Security/SecureObjectSync/SOSPeerCoder.h>
21 #include <Security/SecureObjectSync/SOSInternal.h>
22 #include <Security/SecureObjectSync/SOSRing.h>
23 #include <Security/SecureObjectSync/SOSRingUtils.h>
24 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
25 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
26 #include <Security/SecItemInternal.h>
27 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
28 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
29
30 CFGiblisWithCompareFor(SOSAccount);
31
32 const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
33 const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
34 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
35
36 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
37
38
39 #define DATE_LENGTH 25
40 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
41
42
43 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
44 {
45 bool result = false;
46 CFStringRef circle_name = NULL;
47
48 require_quiet(a, xit);
49 require_quiet(a->factory, xit);
50
51 circle_name = SOSDataSourceFactoryCopyName(a->factory);
52 require(circle_name, xit);
53
54 SOSAccountEnsureCircle(a, circle_name, NULL);
55
56 result = true;
57
58 xit:
59 // We don't own name, so don't release it.
60 CFReleaseNull(circle_name);
61 return result;
62 }
63
64
65 SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
66 CFDictionaryRef gestalt,
67 SOSDataSourceFactoryRef factory) {
68 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
69
70 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
71
72 a->notification_cleanups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
73
74 a->gestalt = CFRetainSafe(gestalt);
75
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);
81
82 a->factory = factory; // We adopt the factory. kthanksbai.
83
84 a->_user_private = NULL;
85 a->_password_tmp = NULL;
86 a->user_private_timer = NULL;
87
88 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
89
90 a->departure_code = kSOSNeverAppliedToCircle;
91
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);
97
98 return a;
99 }
100
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);
107
108 if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
109 updateCircle = true;
110 } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
111 updateCircle = true;
112 } else if(actionCode == kSOSCCSecurityPropertyPending) {
113 updateCircle = true;
114 }
115
116 if (updateCircle) {
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));
120 });
121 }
122
123 errOut:
124 return retval;
125 }
126
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);
132 errOut:
133 return retval;
134 }
135
136 bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
137 {
138 if (CFEqualSafe(new_gestalt, account->gestalt))
139 return false;
140
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));
146 });
147 }
148
149 CFRetainAssign(account->gestalt, new_gestalt);
150 return true;
151 }
152
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);
157
158 return true;
159 }
160
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));
167 });
168 }
169 }
170
171 return true;
172 }
173
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"));
183
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;
190 } else {
191 require_action_quiet(actionCode = kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
192 retval = kSOSCCViewNotMember;
193 }
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;
198 } else {
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;
205 } else {
206 retval = currentStatus;
207 }
208
209 if (updateCircle) {
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));
213 });
214 }
215 }
216
217 errOut:
218 return retval;
219 }
220
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);
226 errOut:
227 return retval;
228 }
229
230 static void dumpViewSet(CFStringRef label, CFSetRef views) {
231 if(views) {
232 secnotice("circleChange", "%@ list: %@", label, views);
233 } else {
234 secnotice("circleChange", "No %@ list provided.", label);
235 }
236 }
237
238 bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef enabledViews, CFSetRef disabledViews) {
239 bool updateCircle = false;
240 dumpViewSet(CFSTR("Enabled"), enabledViews);
241 dumpViewSet(CFSTR("Disabled"), disabledViews);
242
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"));
246
247 // Copy my views
248 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
249 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
250
251 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
252
253
254 if(!SOSPeerInfoVersionIsCurrent(pi)) {
255 if(!SOSPeerInfoUpdateToV2(pi, NULL)) {
256 secnotice("views", "Unable to update peer to V2- can't update views");
257 return false;
258 }
259 }
260
261 if(enabledViews) updateCircle = SOSViewSetEnable(pi, enabledViews);
262 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
263
264 /* UPDATE FULLPEERINFO VIEWS */
265
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));
270 });
271 }
272
273 errOut:
274 return updateCircle;
275 }
276
277
278 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
279 CFDictionaryRef gestalt,
280 SOSDataSourceFactoryRef factory) {
281 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
282
283
284 SOSAccountEnsureFactoryCircles(a);
285
286 return a;
287 }
288
289 static void SOSAccountDestroy(CFTypeRef aObj) {
290 SOSAccountRef a = (SOSAccountRef) aObj;
291
292 // We don't own the factory, merely have a reference to the singleton
293 // Don't free it.
294 // a->factory
295
296 SOSAccountCleanupNotificationForAllPeers(a);
297
298 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
299
300 if (engine)
301 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
302
303 dispatch_sync(a->queue, ^{
304 CFReleaseNull(a->gestalt);
305
306 CFReleaseNull(a->my_identity);
307 CFReleaseNull(a->trusted_circle);
308 CFReleaseNull(a->trusted_rings);
309 CFReleaseNull(a->backups);
310 CFReleaseNull(a->retirees);
311
312 a->user_public_trusted = false;
313 CFReleaseNull(a->user_public);
314 CFReleaseNull(a->user_key_parameters);
315
316 SOSAccountPurgePrivateCredential(a);
317 CFReleaseNull(a->previous_public);
318 CFReleaseNull(a->_user_private);
319 CFReleaseNull(a->_password_tmp);
320
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);
328
329 dispatch_release(a->user_private_timer);
330 CFReleaseNull(a->change_blocks);
331 CFReleaseNull(a->expansion);
332
333 });
334 }
335
336 static OSStatus do_delete(CFDictionaryRef query) {
337 OSStatus result;
338
339 result = SecItemDelete(query);
340 if (result) {
341 secerror("SecItemDelete: %d", (int)result);
342 }
343 return result;
344 }
345
346 static int
347 do_keychain_delete_aks_bags()
348 {
349 OSStatus result;
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,
357 NULL);
358
359 result = do_delete(item);
360 CFReleaseSafe(item);
361
362 return result;
363 }
364
365 static int
366 do_keychain_delete_identities()
367 {
368 OSStatus result;
369 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
370 kSecClass, kSecClassKey,
371 kSecAttrSynchronizable, kCFBooleanTrue,
372 kSecUseTombstones, kCFBooleanFalse,
373 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
374 NULL);
375
376 result = do_delete(item);
377 CFReleaseSafe(item);
378
379 return result;
380 }
381
382 static int
383 do_keychain_delete_lakitu()
384 {
385 OSStatus result;
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"),
393 NULL);
394
395 result = do_delete(item);
396 CFReleaseSafe(item);
397
398 return result;
399 }
400
401 static int
402 do_keychain_delete_sbd()
403 {
404 OSStatus result;
405 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
406 kSecClass, kSecClassGenericPassword,
407 kSecAttrSynchronizable, kCFBooleanTrue,
408 kSecUseTombstones, kCFBooleanFalse,
409 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
410 NULL);
411
412 result = do_delete(item);
413 CFReleaseSafe(item);
414
415 return result;
416 }
417
418 void SOSAccountSetToNew(SOSAccountRef a) {
419 secnotice("accountChange", "Setting Account to New");
420 int result = 0;
421
422 CFReleaseNull(a->my_identity);
423 CFReleaseNull(a->trusted_circle);
424 CFReleaseNull(a->trusted_rings);
425 CFReleaseNull(a->backups);
426 CFReleaseNull(a->retirees);
427
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);
433
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);
439
440 /* remove all syncable items */
441 result = do_keychain_delete_aks_bags();
442 secdebug("set to new", "result for deleting aks bags: %d", result);
443
444 result = do_keychain_delete_identities();
445 secdebug("set to new", "result for deleting identities: %d", result);
446
447 result = do_keychain_delete_lakitu();
448 secdebug("set to new", "result for deleting lakitu: %d", result);
449
450 result = do_keychain_delete_sbd();
451 secdebug("set to new", "result for deleting sbd: %d", result);
452
453 a->user_public_trusted = false;
454 a->departure_code = kSOSNeverAppliedToCircle;
455 a->user_private_timer = 0;
456 a->lock_notification_token = 0;
457
458 // keeping gestalt;
459 // keeping factory;
460 // Live Notification
461 // change_blocks;
462 // update_interest_block;
463 // update_block;
464
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;
469
470 a->trusted_rings = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
471 a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
472
473 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
474 a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
475
476 SOSAccountEnsureFactoryCircles(a); // Does rings too
477 }
478
479
480 static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
481 SOSAccountRef a = (SOSAccountRef) aObj;
482
483 CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
484
485 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a, gestaltDescription, a->trusted_circle, a->my_identity);
486
487 CFReleaseNull(gestaltDescription);
488
489 return result;
490 }
491
492 CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
493
494 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
495
496 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
497
498 CFReleaseNull(gestaltDescription);
499
500 return result;
501 }
502
503 static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
504 {
505 SOSAccountRef laccount = (SOSAccountRef) lhs;
506 SOSAccountRef raccount = (SOSAccountRef) rhs;
507
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);
512 }
513
514 dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
515 return account->queue;
516 }
517
518 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
519 account->user_public_trusted = true;
520 }
521
522 SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
523 {
524 return CFRetainSafe(account->my_identity);
525 }
526
527 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
528 bool ok = false;
529 __block CFErrorRef error = NULL;
530
531 if (!SOSAccountHasPublicKey(account, &error)) {
532 CFReleaseSafe(error);
533 return false;
534 }
535
536 bool hasID = true;
537
538 require_action_quiet(account->my_identity, xit,
539 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
540
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){
544 hasID = false;
545 secerror("Cannot sync with all peers at this time, securityd needs the IDS device ID first.");
546
547 __block bool success = false;
548
549 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
550 success = (sync_error == NULL);
551 if (!success) {
552 CFRetainAssign(error, sync_error);
553 }
554 });
555
556 if(!success){
557 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", error);
558 }
559 else{
560 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
561 }
562 }
563 CFReleaseNull(deviceID);
564 }
565
566 require_action_quiet(account->trusted_circle, xit,
567 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
568
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);
573 xit:
574 if (!ok) {
575 secerror("sync with device failure: %@", error);
576 }
577 CFReleaseSafe(error);
578 return ok;
579 }
580
581 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef circleName, CFStringRef peerID) {
582 SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
583 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
584
585 return myPeerID && CFEqualSafe(myPeerID, peerID);
586 }
587
588 static bool isDefaultsWriteSetupToSyncOverIDS(){
589 return ((whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent));
590 }
591
592 bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
593 {
594 bool result = true;
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);
601
602 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
603 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
604 NULL, &localError));
605
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);
613 } else {
614 CFArrayAppendValue(peerIds, SOSPeerInfoGetPeerID(peer));
615 }
616 }
617 });
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);
622 }
623
624 SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
625 result = SOSEngineSyncWithPeers(engine, account->ids_message_transport, account->kvs_message_transport, &localError);
626
627 result &= ((SyncingCompletedOverIDS) &&
628 (SyncingCompletedOverKVS || (CFDictionaryGetCount(circleToPeerIDs) == 0)));
629
630 if (result)
631 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
632
633 xit:
634 CFReleaseNull(circleToPeerIDs);
635
636 if (!result) {
637 secdebug("Account", "Could not sync with all peers: %@", localError);
638 CFErrorPropagate(localError, error);
639 localError = NULL;
640 }
641 CFReleaseNull(peerIds);
642 CFReleaseSafe(localError);
643 return result;
644 }
645
646 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
647 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
648 {
649 bool success = true;
650
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);
654
655 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
656
657 CFStringRef circle_name = SOSCircleGetName(circle);
658
659 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
660 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
661
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);
665 }
666
667 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
668 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
669 }
670
671 CFReleaseNull(localError);
672
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);
676 }
677 }
678 CFReleaseNull(localError);
679 CFReleaseNull(circleToPeerIDs);
680
681 xit:
682 return success;
683 }
684
685 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
686 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
687
688 __block bool success = true;
689
690 CFSetForEach(account->retirees, ^(const void *value) {
691 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
692
693 if (retiree) {
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);
698 };
699 }
700 });
701
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);
706 });
707
708 CFReleaseNull(retirees_to_remove);
709
710 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
711 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
712 NULL);
713
714 CFReleaseNull(retirees_to_cleanup);
715
716 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
717
718 CFReleaseNull(retirements_to_remove);
719
720 return success;
721 }
722
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);
729 }
730 CFReleaseSafe(cleanupError);
731 });
732 return true;
733 }
734
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;
738
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);
744 }
745 });
746 }
747
748 if(SOSCircleCountPeers(new_circle) == 0) {
749 SOSCircleResetToEmpty(new_circle, NULL);
750 }
751
752 return new_circle;
753 }
754
755 //
756 // MARK: Circle Membership change notificaion
757 //
758
759 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
760 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
761 CFArrayAppendValue(a->change_blocks, copy);
762 CFReleaseNull(copy);
763 }
764
765 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
766 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
767 }
768
769 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
770 if (!changeBlock) return;
771
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) {
776
777 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
778 return;
779
780 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
781 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
782
783 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
784 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
785 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
786
787 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
788 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
789 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
790 });
791
792 CFSetForEach(added_peers, ^(const void *value) {
793 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
794 });
795
796 CFSetForEach(removed_peers, ^(const void *value) {
797 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
798 });
799 }
800
801 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
802 changeBlock(peer_ids, added_ids, removed_ids);
803
804 CFReleaseSafe(peer_ids);
805 CFReleaseSafe(added_ids);
806 CFReleaseSafe(removed_ids);
807 };
808
809 CFRetainSafe(changeBlock);
810 SOSAccountAddChangeBlock(a, block_to_register);
811
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);
815 }
816 CFReleaseSafe(empty);
817 }
818
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);
825 }
826 CFReleaseNull(purgeError);
827
828 CFReleaseNull(account->my_identity);
829 }
830 }
831
832 bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
833 SOSFullPeerInfoRef fpi = account->my_identity;
834 if(!fpi) return false;
835
836 CFErrorRef localError = NULL;
837
838 bool retval = false;
839
840 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
841 if (!retire_peer) {
842 secerror("Create ticket failed for peer %@: %@", fpi, localError);
843 } else {
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);
853 }
854 CFReleaseSafe(cleanupError);
855 }
856 }
857
858 // Store the retirement record locally.
859 CFSetAddValue(account->retirees, retire_peer);
860
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);
865 }
866 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
867 secwarning("Couldn't flush retirement data (%@)", postError);
868 }
869 CFReleaseNull(postError);
870 }
871
872 SOSAccountPurgeIdentity(account);
873
874 retval = true;
875
876 CFReleaseNull(localError);
877 CFReleaseNull(retire_peer);
878 return retval;
879 }
880
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);
886
887 CFErrorRef localError = NULL;
888
889 bool retval = false;
890 bool writeRing = false;
891 bool writePeerInfo = false;
892
893 if(SOSRingHasPeerID(ring, peerID)) {
894 writePeerInfo = true;
895 }
896
897 #if 0
898 // this was circle behavior - at some point
899 if(SOSRingHasApplicant(ring, peerID)) {
900 writeRing = true;
901 }
902 #endif
903
904 if(writePeerInfo || writeRing) {
905 SOSRingWithdraw(ring, NULL, fpi, error);
906 }
907
908 // Write leave thing to Transport
909 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
910 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
911
912 if (writeRing) {
913 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
914
915 if (ring_data) {
916 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
917 }
918 CFReleaseNull(ring_data);
919 }
920 retval = true;
921 CFReleaseNull(localError);
922 return retval;
923 }
924
925 bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
926 bool result = false;
927 SOSTransportCircleRef transport = account->circle_transport;
928 if (transport) {
929 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
930 }
931 return result;
932 }
933
934 /*
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.
940
941 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
942 with server but initial round trip with server does not imply
943 NSUbiquitousKeyValueStoreInitialSyncChange.
944 */
945
946
947 //
948 // MARK: Status summary
949 //
950
951 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
952 if (!circle)
953 return kSOSCCNotInCircle;
954
955 if (circle && SOSCircleCountPeers(circle) == 0)
956 return kSOSCCCircleAbsent;
957
958 if (this_peer) {
959
960 if(SOSPeerInfoIsRetirementTicket(this_peer))
961 return kSOSCCNotInCircle;
962
963 if (SOSCircleHasPeer(circle, this_peer, NULL))
964 return kSOSCCInCircle;
965
966 if (SOSCircleHasApplicant(circle, this_peer, NULL))
967 return kSOSCCRequestPending;
968 }
969
970 return kSOSCCNotInCircle;
971 }
972
973 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
974 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
975
976 if (result != kSOSCCInCircle && result != kSOSCCError) {
977 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
978 return false;
979 }
980
981 return true;
982 }
983
984 SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
985 if (!SOSAccountHasPublicKey(account, error)) {
986 return kSOSCCError;
987 }
988
989 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
990 }
991
992 //
993 // MARK: Account Reset Circles
994 //
995
996 static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef user_key, CFErrorRef *error) {
997 bool result = false;
998
999 require(SOSAccountHasCircle(account, error), fail);
1000 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1001
1002 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1003 bool result = false;
1004 SOSFullPeerInfoRef cloud_identity = NULL;
1005 CFErrorRef localError = NULL;
1006
1007 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1008
1009 {
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);
1015 }
1016
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);
1020 result = true;
1021 SOSAccountPublishCloudParameters(account, NULL);
1022
1023 err_out:
1024 if (result == false)
1025 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1026 if (localError && error && *error == NULL) {
1027 *error = localError;
1028 localError = NULL;
1029 }
1030 CFReleaseNull(localError);
1031 CFReleaseNull(cloud_identity);
1032 return result;
1033 });
1034
1035 result = true;
1036
1037 fail:
1038 return result;
1039 }
1040
1041
1042 bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
1043 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1044 if (!user_key)
1045 return false;
1046
1047 CFReleaseNull(account->my_identity);
1048
1049 return user_key && SOSAccountResetCircleToOffering(account, user_key, error);
1050 }
1051
1052 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1053 if (!SOSAccountHasPublicKey(account, error))
1054 return false;
1055
1056 CFReleaseNull(account->my_identity);
1057
1058 __block bool result = true;
1059 account->departure_code = kSOSWithdrewMembership;
1060 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1061 result = SOSCircleResetToEmpty(circle, error);
1062 return result;
1063 });
1064
1065 if (!result) {
1066 secerror("error: %@", error ? *error : NULL);
1067 }
1068
1069 return result;
1070 }
1071
1072
1073 //
1074 // MARK: Waiting for in-sync
1075 //
1076
1077 static bool SOSAccountHasBeenInSync(SOSAccountRef account) {
1078 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1079 CFSetRef unsynced = asSet(unsyncedObject, NULL);
1080
1081 return !(unsyncedObject == kCFBooleanTrue || (unsynced && (CFSetGetCount(unsynced) > 0)));
1082 }
1083
1084 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account, CFSetRef viewsInSync) {
1085 bool notifyOfChange = false;
1086
1087 SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
1088 bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
1089
1090 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1091
1092 if (!inOrApplying) {
1093 if (unsyncedObject != NULL) {
1094 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1095 secnotice("initial-sync", "in sync, clearing pending");
1096 notifyOfChange = true;
1097 }
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);
1103 });
1104 });
1105
1106 if (viewsInSync) {
1107 CFSetSubtract(viewsToSync, viewsInSync);
1108
1109 }
1110
1111 if (unsyncedObject == kCFBooleanTrue) {
1112 if (CFSetGetCount(viewsToSync) == 0) {
1113 secnotice("initial-sync", "No views to wait for");
1114 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1115 } else {
1116 __block CFSetRef newViews = NULL;
1117 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account), ^(CFSetRef enabled) {
1118 newViews = CFSetCreateIntersection(kCFAllocatorDefault, enabled, viewsToSync);
1119 });
1120 secnotice("initial-sync", "Pending views set from True: %@", newViews);
1121 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
1122 CFReleaseNull(newViews);
1123 }
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;
1132 }
1133 CFReleaseNull(newViews);
1134 }
1135
1136 CFReleaseNull(viewsToSync);
1137 }
1138
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;
1144 }
1145
1146 return SOSAccountHasBeenInSync(account);
1147 }
1148
1149 static void SOSAccountPeerGotInSync(SOSAccountRef account, CFStringRef peerID) {
1150 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID);
1151
1152 if (account->trusted_circle) {
1153 SOSPeerInfoRef peer = SOSCircleCopyPeerWithID(account->trusted_circle, peerID, NULL);
1154 if (peer) {
1155 CFSetRef views = SOSPeerInfoCopyEnabledViews(peer);
1156 SOSAccountUpdateOutOfSyncViews(account, views);
1157 CFReleaseNull(views);
1158 }
1159 CFReleaseNull(peer);
1160 }
1161 }
1162
1163 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account) {
1164 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1165
1166 CFDictionaryForEach(account->notification_cleanups, ^(const void *key, const void *value) {
1167 if (engine) {
1168 SOSEngineSetSyncCompleteListener(engine, key, NULL);
1169 }
1170 dispatch_async(account->queue, value);
1171 });
1172
1173 CFDictionaryRemoveAllValues(account->notification_cleanups);
1174 }
1175
1176 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account, CFStringRef peerID) {
1177 dispatch_block_t cleanup = CFDictionaryGetValue(account->notification_cleanups, peerID);
1178
1179 if (cleanup) {
1180 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1181
1182 if (engine) {
1183 SOSEngineSetSyncCompleteListener(engine, peerID, NULL);
1184 }
1185
1186 dispatch_async(account->queue, cleanup);
1187 }
1188
1189 CFDictionaryRemoveValue(account->notification_cleanups, peerID);
1190
1191 }
1192
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);
1197 }
1198
1199 void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
1200 if (CFDictionaryGetCount(account->notification_cleanups) == 0) {
1201 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1202
1203 SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
1204
1205 if (engine) {
1206 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1207 CFStringRef peerID = CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerID(peer));
1208
1209 SOSAccountRegisterCleanupBlock(account, peerID, ^{
1210 CFReleaseSafe(peerID);
1211 });
1212
1213 SOSEngineSetSyncCompleteListener(engine, peerID, ^{
1214 SOSAccountPeerGotInSync(account, peerID);
1215 SOSAccountCleanupNotificationForPeer(account, peerID);
1216 SOSAccountFinishTransaction(account);
1217 });
1218 });
1219 } else {
1220 secerror("Couldn't find engine to setup notifications!!!");
1221 }
1222 }
1223 }
1224
1225 void SOSAccountCancelSyncChecking(SOSAccountRef account) {
1226 SOSAccountCleanupNotificationForAllPeers(account);
1227 SOSAccountUpdateOutOfSyncViews(account, NULL);
1228 }
1229
1230 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account) {
1231 bool hasBeenInSync = false;
1232
1233 if (!SOSAccountIsInCircle(account, NULL)) {
1234 SOSAccountCancelSyncChecking(account);
1235 } else {
1236 hasBeenInSync = SOSAccountHasBeenInSync(account);
1237 if (!hasBeenInSync) {
1238 hasBeenInSync = SOSAccountUpdateOutOfSyncViews(account, NULL);
1239 if (hasBeenInSync) {
1240 // Cancel and declare victory
1241 SOSAccountCancelSyncChecking(account);
1242 } else {
1243 // Make sure we're watching in case this is the fist attempt
1244 SOSAccountEnsureSyncChecking(account);
1245 }
1246 }
1247 }
1248
1249 return hasBeenInSync;
1250 }
1251
1252 //
1253 // MARK: Joining
1254 //
1255
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;
1260
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);
1263
1264 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1265
1266 if (use_cloud_peer) {
1267 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1268 } else {
1269 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1270 }
1271
1272 if (SOSCircleCountPeers(account->trusted_circle) == 0) {
1273 result = SOSAccountResetCircleToOffering(account, user_key, error);
1274 } else {
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);
1284 finish:
1285 if (localError){
1286 secerror("Failed to join with cloud identity: %@", localError);
1287 CFReleaseNull(localError);
1288 }
1289 }
1290 return result;
1291 });
1292 }
1293
1294 fail:
1295 CFReleaseNull(cloud_full_peer);
1296 return result;
1297 }
1298
1299 static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
1300 bool success = false;
1301
1302 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1303 require_quiet(user_key, done); // Fail if we don't get one.
1304
1305 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1306
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);
1311
1312 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1313
1314 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1315 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1316
1317 CFReleaseNull(account->my_identity);
1318 myPeer = NULL;
1319 }
1320 }
1321
1322 success = SOSAccountJoinCircle(account, user_key, use_cloud_identity, error);
1323
1324 require_quiet(success, done);
1325
1326 account->departure_code = kSOSNeverLeftCircle;
1327
1328 done:
1329 return success;
1330 }
1331
1332 bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
1333 return SOSAccountJoinCircles_internal(account, false, error);
1334 }
1335
1336 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1337 CFStringRef result = NULL;
1338
1339 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1340
1341 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1342
1343 fail:
1344 return result;
1345 }
1346
1347 bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
1348 bool result = true;
1349
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")));
1354
1355 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1356
1357 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1358 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDS, error);
1359 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1360
1361 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1362 });
1363 }
1364 else
1365 result = false;
1366 }
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")));
1371
1372 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1373
1374 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1375 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeKVS, error);
1376 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1377
1378 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1379 });
1380 }
1381 else
1382 result = false;
1383
1384 }
1385
1386 SOSCCSyncWithAllPeers();
1387
1388 fail:
1389 return result;
1390 }
1391
1392
1393 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1394 bool result = true;
1395 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1396 //construct message dictionary, circle -> peerID -> message
1397
1398 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1399 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1400
1401 CFStringRef operation = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1402 CFDataRef operationData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, operation, kCFStringEncodingUTF8, 0);
1403
1404 CFDataRef messageData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
1405 CFMutableDataRef mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(operationData) + CFDataGetLength(messageData));
1406
1407 CFDataAppend(mutableData, operationData);
1408 CFDataAppend(mutableData, messageData);
1409
1410 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1411 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableData);
1412 });
1413
1414 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1415 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1416
1417 CFReleaseNull(operation);
1418 CFReleaseNull(mutableData);
1419 CFReleaseNull(operationData);
1420 CFReleaseNull(messageData);
1421 CFReleaseNull(peerToMessage);
1422 CFReleaseNull(circleToPeerMessages);
1423 }
1424 return result;
1425 }
1426
1427 bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1428 bool result = true;
1429 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1430 //construct message dictionary, circle -> peerID -> message
1431
1432 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1433 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1434
1435 CFStringRef operation = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1436 CFDataRef operationData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, operation, kCFStringEncodingUTF8, 0);
1437
1438 CFDataRef messageData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
1439 CFMutableDataRef mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(operationData) + CFDataGetLength(messageData));
1440
1441 CFDataAppend(mutableData, operationData);
1442 CFDataAppend(mutableData, messageData);
1443
1444 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1445 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableData);
1446 });
1447
1448 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1449 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1450
1451 CFReleaseNull(operation);
1452 CFReleaseNull(mutableData);
1453 CFReleaseNull(operationData);
1454 CFReleaseNull(messageData);
1455 CFReleaseNull(peerToMessage);
1456 CFReleaseNull(circleToPeerMessages);
1457 }
1458 return result;
1459 }
1460
1461 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
1462 bool result = true;
1463 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1464
1465 __block bool success = false;
1466
1467 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1468 dispatch_retain(wait_for); // Both this scope and the block own it
1469
1470 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1471 success = (sync_error == NULL);
1472 if (error) {
1473 CFRetainAssign(*error, sync_error);
1474 }
1475
1476 dispatch_semaphore_signal(wait_for);
1477 dispatch_release(wait_for);
1478 });
1479
1480 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1481 dispatch_release(wait_for);
1482
1483 if(!success){
1484 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", *error);
1485 }
1486 else{
1487 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1488 }
1489 }
1490 return result;
1491 }
1492
1493 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
1494 return SOSAccountJoinCircles_internal(account, true, error);
1495 }
1496
1497
1498 bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1499 {
1500 bool result = true;
1501
1502 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1503 return sosAccountLeaveCircle(account, circle, error);
1504 });
1505
1506 account->departure_code = kSOSWithdrewMembership;
1507
1508 return result;
1509 }
1510
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);
1520 });
1521 });
1522 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1523 dispatch_group_wait(group, milestone);
1524
1525 account->departure_code = kSOSWithdrewMembership;
1526
1527 dispatch_release(group);
1528 return result;
1529 }
1530
1531
1532 //
1533 // MARK: Application
1534 //
1535
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)) {
1548 modified = true;
1549 }
1550 }
1551 });
1552 return modified;
1553 });
1554 }
1555 if (peer_error)
1556 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1557 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1558 }
1559
1560 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1561 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1562 if (!user_key)
1563 return false;
1564
1565 __block bool success = true;
1566 __block int64_t num_peers = 0;
1567
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);
1570 if (!accepted)
1571 success = false;
1572 else
1573 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1574 return accepted;
1575 });
1576
1577 return success;
1578 }
1579
1580 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1581 __block bool success = true;
1582 __block int64_t num_peers = 0;
1583
1584 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1585 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1586 if (!rejected)
1587 success = false;
1588 else
1589 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1590 return rejected;
1591 });
1592
1593 return success;
1594 }
1595
1596
1597 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1598 return CFSTR("We're compatible, go away");
1599 }
1600
1601 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1602 return account->departure_code;
1603 }
1604
1605 void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
1606 account->departure_code = reason;
1607 }
1608
1609
1610 CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
1611 CFArrayRef result = NULL;
1612 CFNumberRef generation = NULL;
1613
1614 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1615 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1616
1617 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
1618 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1619
1620 fail:
1621 return result;
1622 }
1623
1624 bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
1625 if (!SOSAccountHasPublicKey(account, error))
1626 return NULL;
1627
1628 return account->user_public_trusted;
1629 }
1630
1631 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
1632 // TODO: this result is never set or used
1633 bool result = true;
1634
1635 secnotice("updates", "Ensuring peer registration.");
1636
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);
1641
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
1646
1647 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1648
1649 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1650 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1651 CFErrorRef localError = NULL;
1652 SOSTransportMessageRef messageTransport = NULL;
1653
1654 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1655 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
1656 }
1657 else
1658 messageTransport = account->kvs_message_transport;
1659
1660 SOSPeerCoderInitializeForPeer(messageTransport->engine, account->my_identity, peer, &localError);
1661 if (localError)
1662 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
1663 CFReleaseSafe(localError);
1664 }
1665 });
1666
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){
1671
1672 __block bool success = false;
1673 __block CFErrorRef localError = NULL;
1674
1675 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1676 success = (sync_error == NULL);
1677 if (localError) {
1678 CFRetainAssign(localError, sync_error);
1679 }
1680 });
1681
1682 if(!success){
1683 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
1684 }
1685 else{
1686 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1687 }
1688 CFReleaseNull(localError);
1689 }
1690 CFReleaseNull(deviceID);
1691 }
1692
1693 done:
1694 return result;
1695 }
1696
1697 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
1698 if (!account->expansion) {
1699 account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
1700 }
1701
1702 return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
1703 }
1704
1705 bool SOSAccountClearValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1706 bool success = SOSAccountEnsureExpansion(account, error);
1707 require_quiet(success, errOut);
1708
1709 CFDictionaryRemoveValue(account->expansion, key);
1710 errOut:
1711 return success;
1712 }
1713
1714 bool SOSAccountSetValue(SOSAccountRef account, const void *key, const void *value, CFErrorRef *error) {
1715 bool success = SOSAccountEnsureExpansion(account, error);
1716 require_quiet(success, errOut);
1717
1718 CFDictionarySetValue(account->expansion, key, value);
1719 errOut:
1720 return success;
1721 }
1722
1723
1724 const void *SOSAccountGetValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1725 if (!account->expansion) {
1726 return NULL;
1727 }
1728 return CFDictionaryGetValue(account->expansion, key);
1729 }
1730
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);
1736 }
1737 CFReleaseSafe(localError);
1738
1739 SOSAccountNotifyEngines(account); // For now our only rings are backup rings.
1740 }
1741
1742 SOSAccountCheckHasBeenInSync(account);
1743
1744 account->circle_rings_retirements_need_attention = false;
1745 }
1746