]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5 /*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10 #include "SOSAccountPriv.h"
11 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
12 #include <Security/SecureObjectSync/SOSTransportCircle.h>
13 #include <Security/SecureObjectSync/SOSTransportMessage.h>
14 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
15 #include <Security/SecureObjectSync/SOSKVSKeys.h>
16 #include <Security/SecureObjectSync/SOSTransport.h>
17 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
18 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
19 #include <Security/SecureObjectSync/SOSEngine.h>
20 #include <Security/SecureObjectSync/SOSPeerCoder.h>
21 #include <Security/SecureObjectSync/SOSInternal.h>
22 #include <Security/SecureObjectSync/SOSRing.h>
23 #include <Security/SecureObjectSync/SOSRingUtils.h>
24 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
25 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
26 #include <Security/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 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
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, CFSetRef excludedViews) {
162 if (account->trusted_circle && account->my_identity) {
163 if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
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
226 retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
227
228 // If that doesn't say we're a member and this view is a V0 subview, and we're syncing V0 views we are a member
229 if (retval != kSOSCCViewMember) {
230 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
231 && SOSAccountSyncingV0(account)) {
232 retval = kSOSCCViewMember;
233 }
234 }
235
236 // If we're only an applicant we report pending if we would be a view member
237 if (retval == kSOSCCViewMember) {
238 bool isApplicant = SOSCircleHasApplicant(account->trusted_circle, SOSAccountGetMyPeerInfo(account), error);
239 if (isApplicant) {
240 retval = kSOSCCViewPending;
241 }
242 }
243
244 errOut:
245 return retval;
246 }
247
248 static void dumpViewSet(CFStringRef label, CFSetRef views) {
249 if(views) {
250 secnotice("circleChange", "%@ list: %@", label, views);
251 } else {
252 secnotice("circleChange", "No %@ list provided.", label);
253 }
254 }
255
256 bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef enabledViews, CFSetRef disabledViews) {
257 bool updateCircle = false;
258 dumpViewSet(CFSTR("Enabled"), enabledViews);
259 dumpViewSet(CFSTR("Disabled"), disabledViews);
260
261 require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
262 require_action_quiet(account->my_identity, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
263 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
264
265 // Copy my views
266 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
267 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
268
269 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
270
271
272 if(!SOSPeerInfoVersionIsCurrent(pi)) {
273 if(!SOSPeerInfoUpdateToV2(pi, NULL)) {
274 secnotice("views", "Unable to update peer to V2- can't update views");
275 return false;
276 }
277 }
278
279 if(enabledViews) updateCircle = SOSViewSetEnable(pi, enabledViews);
280 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
281
282 /* UPDATE FULLPEERINFO VIEWS */
283
284 if (updateCircle && SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL)) {
285 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
286 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
287 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
288 });
289 }
290
291 errOut:
292 return updateCircle;
293 }
294
295
296 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
297 CFDictionaryRef gestalt,
298 SOSDataSourceFactoryRef factory) {
299 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
300
301 SOSAccountEnsureFactoryCircles(a);
302
303 SOSUpdateKeyInterest(a);
304
305 return a;
306 }
307
308 static void SOSAccountDestroy(CFTypeRef aObj) {
309 SOSAccountRef a = (SOSAccountRef) aObj;
310
311 // We don't own the factory, merely have a reference to the singleton
312 // Don't free it.
313 // a->factory
314
315 SOSAccountCleanupNotificationForAllPeers(a);
316
317 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
318
319 if (engine)
320 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
321
322 dispatch_sync(a->queue, ^{
323 CFReleaseNull(a->gestalt);
324
325 CFReleaseNull(a->my_identity);
326 CFReleaseNull(a->trusted_circle);
327 CFReleaseNull(a->trusted_rings);
328 CFReleaseNull(a->backups);
329 CFReleaseNull(a->retirees);
330
331 a->user_public_trusted = false;
332 CFReleaseNull(a->user_public);
333 CFReleaseNull(a->user_key_parameters);
334
335 SOSAccountPurgePrivateCredential(a);
336 CFReleaseNull(a->previous_public);
337 CFReleaseNull(a->_user_private);
338 CFReleaseNull(a->_password_tmp);
339
340 a->departure_code = kSOSNeverAppliedToCircle;
341 CFReleaseNull(a->kvs_message_transport);
342 CFReleaseNull(a->ids_message_transport);
343 CFReleaseNull(a->key_transport);
344 CFReleaseNull(a->circle_transport);
345 dispatch_release(a->queue);
346 CFReleaseNull(a->notification_cleanups);
347
348 dispatch_release(a->user_private_timer);
349 CFReleaseNull(a->change_blocks);
350 CFReleaseNull(a->expansion);
351
352 });
353 }
354
355 static OSStatus do_delete(CFDictionaryRef query) {
356 OSStatus result;
357
358 result = SecItemDelete(query);
359 if (result) {
360 secerror("SecItemDelete: %d", (int)result);
361 }
362 return result;
363 }
364
365 static int
366 do_keychain_delete_aks_bags()
367 {
368 OSStatus result;
369 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
370 kSecClass, kSecClassGenericPassword,
371 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
372 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
373 kSecAttrService, CFSTR("SecureBackupService"),
374 kSecAttrSynchronizable, kCFBooleanTrue,
375 kSecUseTombstones, kCFBooleanFalse,
376 NULL);
377
378 result = do_delete(item);
379 CFReleaseSafe(item);
380
381 return result;
382 }
383
384 static int
385 do_keychain_delete_identities()
386 {
387 OSStatus result;
388 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
389 kSecClass, kSecClassKey,
390 kSecAttrSynchronizable, kCFBooleanTrue,
391 kSecUseTombstones, kCFBooleanFalse,
392 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
393 NULL);
394
395 result = do_delete(item);
396 CFReleaseSafe(item);
397
398 return result;
399 }
400
401 static int
402 do_keychain_delete_lakitu()
403 {
404 OSStatus result;
405 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
406 kSecClass, kSecClassGenericPassword,
407 kSecAttrSynchronizable, kCFBooleanTrue,
408 kSecUseTombstones, kCFBooleanFalse,
409 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
410 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
411 kSecAttrService, CFSTR("EscrowService"),
412 NULL);
413
414 result = do_delete(item);
415 CFReleaseSafe(item);
416
417 return result;
418 }
419
420 static int
421 do_keychain_delete_sbd()
422 {
423 OSStatus result;
424 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
425 kSecClass, kSecClassGenericPassword,
426 kSecAttrSynchronizable, kCFBooleanTrue,
427 kSecUseTombstones, kCFBooleanFalse,
428 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
429 NULL);
430
431 result = do_delete(item);
432 CFReleaseSafe(item);
433
434 return result;
435 }
436
437 void SOSAccountSetToNew(SOSAccountRef a) {
438 secnotice("accountChange", "Setting Account to New");
439 int result = 0;
440
441 CFReleaseNull(a->my_identity);
442 CFReleaseNull(a->trusted_circle);
443 CFReleaseNull(a->trusted_rings);
444 CFReleaseNull(a->backups);
445 CFReleaseNull(a->retirees);
446
447 CFReleaseNull(a->user_key_parameters);
448 CFReleaseNull(a->user_public);
449 CFReleaseNull(a->previous_public);
450 CFReleaseNull(a->_user_private);
451 CFReleaseNull(a->_password_tmp);
452
453 CFReleaseNull(a->key_transport);
454 CFReleaseNull(a->circle_transport);
455 CFReleaseNull(a->kvs_message_transport);
456 CFReleaseNull(a->ids_message_transport);
457 CFReleaseNull(a->expansion);
458
459 /* remove all syncable items */
460 result = do_keychain_delete_aks_bags();
461 secdebug("set to new", "result for deleting aks bags: %d", result);
462
463 result = do_keychain_delete_identities();
464 secdebug("set to new", "result for deleting identities: %d", result);
465
466 result = do_keychain_delete_lakitu();
467 secdebug("set to new", "result for deleting lakitu: %d", result);
468
469 result = do_keychain_delete_sbd();
470 secdebug("set to new", "result for deleting sbd: %d", result);
471
472 a->user_public_trusted = false;
473 a->departure_code = kSOSNeverAppliedToCircle;
474 a->user_private_timer = 0;
475 a->lock_notification_token = 0;
476
477 // keeping gestalt;
478 // keeping factory;
479 // Live Notification
480 // change_blocks;
481 // update_interest_block;
482 // update_block;
483
484 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
485 a->circle_transport = NULL;
486 a->kvs_message_transport = NULL;
487 a->ids_message_transport = NULL;
488
489 a->trusted_rings = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
490 a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
491
492 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
493 a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
494
495 SOSAccountEnsureFactoryCircles(a); // Does rings too
496
497 SOSUpdateKeyInterest(a);
498 }
499
500
501 static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
502 SOSAccountRef a = (SOSAccountRef) aObj;
503
504 CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
505
506 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a, gestaltDescription, a->trusted_circle, a->my_identity);
507
508 CFReleaseNull(gestaltDescription);
509
510 return result;
511 }
512
513 CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
514
515 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
516
517 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
518
519 CFReleaseNull(gestaltDescription);
520
521 return result;
522 }
523
524 static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
525 {
526 SOSAccountRef laccount = (SOSAccountRef) lhs;
527 SOSAccountRef raccount = (SOSAccountRef) rhs;
528
529 return CFEqualSafe(laccount->gestalt, raccount->gestalt)
530 && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
531 && CFEqualSafe(laccount->trusted_rings, raccount->trusted_rings)
532 && CFEqualSafe(laccount->my_identity, raccount->my_identity);
533 }
534
535 dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
536 return account->queue;
537 }
538
539 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
540 account->user_public_trusted = true;
541 }
542
543 SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
544 {
545 return CFRetainSafe(account->my_identity);
546 }
547
548 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
549 bool ok = false;
550 __block CFErrorRef error = NULL;
551
552 if (!SOSAccountHasPublicKey(account, &error)) {
553 CFReleaseSafe(error);
554 return false;
555 }
556
557 bool hasID = true;
558
559 require_action_quiet(account->my_identity, xit,
560 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
561
562 SOSTransportMessageIDSGetIDSDeviceID(account);
563
564 require_action_quiet(account->trusted_circle, xit,
565 SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
566
567 require_action_quiet(hasID, xit,
568 SOSCreateError(kSOSErrorBadFormat, CFSTR("Missing IDS device ID"), NULL, &error));
569 ok = SOSCircleHasPeerWithID(account->trusted_circle,
570 SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity)), &error);
571 xit:
572 if (!ok) {
573 secerror("sync with device failure: %@", error);
574 }
575 CFReleaseSafe(error);
576 return ok;
577 }
578
579 static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef peerID) {
580 SOSPeerInfoRef mypi = SOSFullPeerInfoGetPeerInfo(account->my_identity);
581 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
582
583 return myPeerID && CFEqualSafe(myPeerID, peerID);
584 }
585
586 static bool isDefaultsWriteSetupToSyncOverIDS(){
587 return ((whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent));
588 }
589
590 bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
591 {
592 bool result = true;
593 __block bool SyncingCompletedOverIDS = true;
594 __block bool SyncingCompletedOverKVS = true;
595 __block CFErrorRef localError = NULL;
596 SOSCircleRef circle = SOSAccountGetCircle(account, error);
597 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
598 CFMutableArrayRef peerIds = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
599
600 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
601 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
602 NULL, &localError));
603
604 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
605 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
606 if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)) {
607 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
608 CFMutableDictionaryRef circleToIdsId = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
609 CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
610 CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
611 CFDictionaryAddValue(circleToIdsId, SOSCircleGetName(circle), ids);
612 SyncingCompletedOverIDS = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToIdsId, &localError);
613 CFReleaseNull(circleToIdsId);
614 } else {
615 CFArrayAppendValue(peerIds, SOSPeerInfoGetPeerID(peer));
616 }
617 }
618 });
619 if (CFArrayGetCount(peerIds)) {
620 secnotice("KVS", "Syncing with KVS capable peers");
621 CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(circle), peerIds);
622 SyncingCompletedOverKVS &= SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
623 }
624
625 SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
626 result = SOSEngineSyncWithPeers(engine, account->ids_message_transport, account->kvs_message_transport, &localError);
627
628 result &= ((SyncingCompletedOverIDS) &&
629 (SyncingCompletedOverKVS || (CFDictionaryGetCount(circleToPeerIDs) == 0)));
630
631 if (result)
632 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
633
634 xit:
635 CFReleaseNull(circleToPeerIDs);
636
637 if (!result) {
638 secdebug("Account", "Could not sync with all peers: %@", localError);
639 CFErrorPropagate(localError, error);
640 localError = NULL;
641 }
642 CFReleaseNull(peerIds);
643 CFReleaseSafe(localError);
644 return result;
645 }
646
647 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
648 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
649 {
650 bool success = true;
651
652 SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
653 require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
654 require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
655
656 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
657
658 CFStringRef circle_name = SOSCircleGetName(circle);
659
660 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
661 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
662
663 CFErrorRef localError = NULL;
664 if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
665 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
666 }
667
668 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
669 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
670 }
671
672 CFReleaseNull(localError);
673
674 if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
675 if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
676 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
677 }
678 }
679 CFReleaseNull(localError);
680 CFReleaseNull(circleToPeerIDs);
681
682 xit:
683 return success;
684 }
685
686 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
687 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
688
689 __block bool success = true;
690
691 CFSetForEach(account->retirees, ^(const void *value) {
692 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
693
694 if (retiree) {
695 // 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.
696 if (!SOSPeerInfoIsRetirementTicket(retiree) ||
697 (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
698 CFSetAddValue(retirees_to_remove, retiree);
699 };
700 }
701 });
702
703 CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
704 CFSetForEach(retirees_to_remove, ^(const void *value) {
705 CFArrayAppendValue(retirees_to_cleanup, value);
706 CFSetRemoveValue(account->retirees, value);
707 });
708
709 CFReleaseNull(retirees_to_remove);
710
711 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
712 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
713 NULL);
714
715 CFReleaseNull(retirees_to_cleanup);
716
717 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
718
719 CFReleaseNull(retirements_to_remove);
720
721 return success;
722 }
723
724 bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
725 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
726 CFSetSetValue(account->retirees, peer);
727 CFErrorRef cleanupError = NULL;
728 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
729 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
730 }
731 CFReleaseSafe(cleanupError);
732 });
733 return true;
734 }
735
736 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
737 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
738 if(!new_circle) return NULL;
739
740 if (account->retirees) {
741 CFSetForEach(account->retirees, ^(const void* value) {
742 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
743 if (isSOSPeerInfo(pi)) {
744 SOSCircleUpdatePeerInfo(new_circle, pi);
745 }
746 });
747 }
748
749 if(SOSCircleCountPeers(new_circle) == 0) {
750 SOSCircleResetToEmpty(new_circle, NULL);
751 }
752
753 return new_circle;
754 }
755
756 //
757 // MARK: Circle Membership change notificaion
758 //
759
760 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
761 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
762 CFArrayAppendValue(a->change_blocks, copy);
763 CFReleaseNull(copy);
764 }
765
766 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
767 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
768 }
769
770 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
771 if (!changeBlock) return;
772
773 CFRetainSafe(ds_name);
774 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
775 CFSetRef added_peers, CFSetRef removed_peers,
776 CFSetRef added_applicants, CFSetRef removed_applicants) {
777
778 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
779 return;
780
781 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
782 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
783
784 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
785 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
786 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
787
788 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
789 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
790 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
791 });
792
793 CFSetForEach(added_peers, ^(const void *value) {
794 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
795 });
796
797 CFSetForEach(removed_peers, ^(const void *value) {
798 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
799 });
800 }
801
802 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
803 changeBlock(peer_ids, added_ids, removed_ids);
804
805 CFReleaseSafe(peer_ids);
806 CFReleaseSafe(added_ids);
807 CFReleaseSafe(removed_ids);
808 };
809
810 CFRetainSafe(changeBlock);
811 SOSAccountAddChangeBlock(a, block_to_register);
812
813 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
814 if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
815 block_to_register(a->trusted_circle, empty, empty, empty, empty);
816 }
817 CFReleaseSafe(empty);
818 }
819
820 void SOSAccountPurgeIdentity(SOSAccountRef account) {
821 if (account->my_identity) {
822 // Purge private key but don't return error if we can't.
823 CFErrorRef purgeError = NULL;
824 if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
825 secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
826 }
827 CFReleaseNull(purgeError);
828
829 CFReleaseNull(account->my_identity);
830 }
831 }
832
833 bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
834 SOSFullPeerInfoRef fpi = account->my_identity;
835 if(!fpi) return false;
836
837 CFErrorRef localError = NULL;
838
839 bool retval = false;
840
841 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
842 if (!retire_peer) {
843 secerror("Create ticket failed for peer %@: %@", fpi, localError);
844 } else {
845 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
846 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
847 // Remove our application if we have one.
848 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
849 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
850 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
851 CFErrorRef cleanupError = NULL;
852 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
853 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
854 }
855 CFReleaseSafe(cleanupError);
856 }
857 }
858
859 // Store the retirement record locally.
860 CFSetAddValue(account->retirees, retire_peer);
861
862 // Write retirement to Transport
863 CFErrorRef postError = NULL;
864 if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
865 secwarning("Couldn't post retirement (%@)", postError);
866 }
867 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
868 secwarning("Couldn't flush retirement data (%@)", postError);
869 }
870 CFReleaseNull(postError);
871 }
872
873 SOSAccountPurgeIdentity(account);
874
875 retval = true;
876
877 CFReleaseNull(localError);
878 CFReleaseNull(retire_peer);
879 return retval;
880 }
881
882 bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
883 SOSFullPeerInfoRef fpi = account->my_identity;
884 if(!fpi) return false;
885 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
886 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
887
888 CFErrorRef localError = NULL;
889
890 bool retval = false;
891 bool writeRing = false;
892 bool writePeerInfo = false;
893
894 if(SOSRingHasPeerID(ring, peerID)) {
895 writePeerInfo = true;
896 }
897
898 #if 0
899 // this was circle behavior - at some point
900 if(SOSRingHasApplicant(ring, peerID)) {
901 writeRing = true;
902 }
903 #endif
904
905 if(writePeerInfo || writeRing) {
906 SOSRingWithdraw(ring, NULL, fpi, error);
907 }
908
909 // Write leave thing to Transport
910 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
911 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
912
913 if (writeRing) {
914 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
915
916 if (ring_data) {
917 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
918 }
919 CFReleaseNull(ring_data);
920 }
921 retval = true;
922 CFReleaseNull(localError);
923 return retval;
924 }
925
926 bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
927 bool result = false;
928 SOSTransportCircleRef transport = account->circle_transport;
929 if (transport) {
930 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
931 }
932 return result;
933 }
934
935 /*
936 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
937 local value that has been overwritten by a distant value. If there is no
938 conflict between the local and the distant values when doing the initial
939 sync (e.g. if the cloud has no data stored or the client has not stored
940 any data yet), you'll never see that notification.
941
942 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
943 with server but initial round trip with server does not imply
944 NSUbiquitousKeyValueStoreInitialSyncChange.
945 */
946
947
948 //
949 // MARK: Status summary
950 //
951
952 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
953 if (!circle)
954 return kSOSCCNotInCircle;
955
956 if (circle && SOSCircleCountPeers(circle) == 0)
957 return kSOSCCCircleAbsent;
958
959 if (this_peer) {
960
961 if(SOSPeerInfoIsRetirementTicket(this_peer))
962 return kSOSCCNotInCircle;
963
964 if (SOSCircleHasPeer(circle, this_peer, NULL))
965 return kSOSCCInCircle;
966
967 if (SOSCircleHasApplicant(circle, this_peer, NULL))
968 return kSOSCCRequestPending;
969 }
970
971 return kSOSCCNotInCircle;
972 }
973
974 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
975 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
976
977 if (result != kSOSCCInCircle && result != kSOSCCError) {
978 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
979 return false;
980 }
981
982 return true;
983 }
984
985 SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
986 if (!SOSAccountHasPublicKey(account, error)) {
987 return kSOSCCError;
988 }
989
990 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
991 }
992
993 //
994 // MARK: Account Reset Circles
995 //
996
997 static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef user_key, CFErrorRef *error) {
998 bool result = false;
999
1000 require(SOSAccountHasCircle(account, error), fail);
1001 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1002
1003 (void) SOSAccountResetAllRings(account, error);
1004
1005 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1006 bool result = false;
1007 SOSFullPeerInfoRef cloud_identity = NULL;
1008 CFErrorRef localError = NULL;
1009
1010 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1011
1012 {
1013 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
1014 require_quiet(cloud_peer, err_out);
1015 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
1016 CFReleaseNull(cloud_peer);
1017 require_quiet(cloud_identity, err_out);
1018 }
1019
1020 account->departure_code = kSOSNeverLeftCircle;
1021 require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
1022 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
1023 require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
1024 result = true;
1025 SOSAccountPublishCloudParameters(account, NULL);
1026
1027 err_out:
1028 if (result == false)
1029 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1030 if (localError && error && *error == NULL) {
1031 *error = localError;
1032 localError = NULL;
1033 }
1034 CFReleaseNull(localError);
1035 CFReleaseNull(cloud_identity);
1036 return result;
1037 });
1038
1039 result = true;
1040
1041 fail:
1042 return result;
1043 }
1044
1045
1046 bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
1047 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1048 if (!user_key)
1049 return false;
1050
1051 CFReleaseNull(account->my_identity);
1052
1053 return user_key && SOSAccountResetCircleToOffering(account, user_key, error);
1054 }
1055
1056 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1057 if (!SOSAccountHasPublicKey(account, error))
1058 return false;
1059 __block bool result = true;
1060
1061 result &= SOSAccountResetAllRings(account, error);
1062
1063 CFReleaseNull(account->my_identity);
1064
1065 account->departure_code = kSOSWithdrewMembership;
1066 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1067 result = SOSCircleResetToEmpty(circle, error);
1068 return result;
1069 });
1070
1071 if (!result) {
1072 secerror("error: %@", error ? *error : NULL);
1073 }
1074
1075 return result;
1076 }
1077
1078
1079 //
1080 // MARK: Waiting for in-sync
1081 //
1082
1083 static bool SOSAccountHasBeenInSync(SOSAccountRef account) {
1084 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1085 CFSetRef unsynced = asSet(unsyncedObject, NULL);
1086
1087 return !(unsyncedObject == kCFBooleanTrue || (unsynced && (CFSetGetCount(unsynced) > 0)));
1088 }
1089
1090 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account, CFSetRef viewsInSync) {
1091 bool notifyOfChange = false;
1092
1093 SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
1094 bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
1095
1096 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1097
1098 if (!inOrApplying) {
1099 if (unsyncedObject != NULL) {
1100 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1101 secnotice("initial-sync", "in sync, clearing pending");
1102 notifyOfChange = true;
1103 }
1104 } else if (circleStatus == kSOSCCInCircle) {
1105 __block CFMutableSetRef viewsToSync = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1106 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1107 SOSPeerInfoWithEnabledViewSet(peer, ^(CFSetRef enabled) {
1108 CFSetUnion(viewsToSync, enabled);
1109 });
1110 });
1111
1112 if (viewsInSync) {
1113 CFSetSubtract(viewsToSync, viewsInSync);
1114
1115 }
1116
1117 if (unsyncedObject == kCFBooleanTrue) {
1118 if (CFSetGetCount(viewsToSync) == 0) {
1119 secnotice("initial-sync", "No views to wait for");
1120 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1121 } else {
1122 __block CFSetRef newViews = NULL;
1123 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account), ^(CFSetRef enabled) {
1124 newViews = CFSetCreateIntersection(kCFAllocatorDefault, enabled, viewsToSync);
1125 });
1126 secnotice("initial-sync", "Pending views set from True: %@", newViews);
1127 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
1128 CFReleaseNull(newViews);
1129 }
1130 notifyOfChange = true;
1131 } else if (isSet(unsyncedObject)) {
1132 CFSetRef waiting = (CFMutableSetRef) unsyncedObject;
1133 CFSetRef newViews = CFSetCreateIntersection(kCFAllocatorDefault, waiting, viewsToSync);
1134 if (!CFEqualSafe(waiting, newViews)) {
1135 secnotice("initial-sync", "Pending views updated: %@", newViews);
1136 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
1137 notifyOfChange = true;
1138 }
1139 CFReleaseNull(newViews);
1140 }
1141
1142 CFReleaseNull(viewsToSync);
1143 }
1144
1145 if (notifyOfChange) {
1146 secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification);
1147 notify_post(kSOSCCInitialSyncChangedNotification);
1148 // Make sure we update the engine
1149 account->circle_rings_retirements_need_attention = true;
1150 }
1151
1152 return SOSAccountHasBeenInSync(account);
1153 }
1154
1155 static void SOSAccountPeerGotInSync(SOSAccountRef account, CFStringRef peerID) {
1156 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID);
1157
1158 if (account->trusted_circle) {
1159 SOSPeerInfoRef peer = SOSCircleCopyPeerWithID(account->trusted_circle, peerID, NULL);
1160 if (peer) {
1161 CFSetRef views = SOSPeerInfoCopyEnabledViews(peer);
1162 SOSAccountUpdateOutOfSyncViews(account, views);
1163 CFReleaseNull(views);
1164 }
1165 CFReleaseNull(peer);
1166 }
1167 }
1168
1169 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account) {
1170 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1171
1172 CFDictionaryForEach(account->notification_cleanups, ^(const void *key, const void *value) {
1173 if (engine) {
1174 SOSEngineSetSyncCompleteListener(engine, key, NULL);
1175 }
1176 dispatch_async(account->queue, value);
1177 });
1178
1179 CFDictionaryRemoveAllValues(account->notification_cleanups);
1180 }
1181
1182 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account, CFStringRef peerID) {
1183 dispatch_block_t cleanup = CFDictionaryGetValue(account->notification_cleanups, peerID);
1184
1185 if (cleanup) {
1186 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1187
1188 if (engine) {
1189 SOSEngineSetSyncCompleteListener(engine, peerID, NULL);
1190 }
1191
1192 dispatch_async(account->queue, cleanup);
1193 }
1194
1195 CFDictionaryRemoveValue(account->notification_cleanups, peerID);
1196
1197 }
1198
1199 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account, CFStringRef peerID, dispatch_block_t block) {
1200 dispatch_block_t copy = Block_copy(block);
1201 CFDictionarySetValue(account->notification_cleanups, peerID, copy);
1202 CFReleaseNull(copy);
1203 }
1204
1205 void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
1206 if (CFDictionaryGetCount(account->notification_cleanups) == 0) {
1207 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
1208 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1209
1210 SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
1211
1212 if (engine) {
1213 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1214 CFStringRef peerID = CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerID(peer));
1215
1216 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID);
1217 SOSAccountRegisterCleanupBlock(account, peerID, ^{
1218 CFReleaseSafe(peerID);
1219 });
1220
1221 SOSEngineSetSyncCompleteListener(engine, peerID, ^{
1222 SOSAccountPeerGotInSync(account, peerID);
1223 SOSAccountCleanupNotificationForPeer(account, peerID);
1224 SOSAccountFinishTransaction(account);
1225 });
1226 });
1227 } else {
1228 secerror("Couldn't find engine to setup notifications!!!");
1229 }
1230 }
1231 }
1232
1233 void SOSAccountCancelSyncChecking(SOSAccountRef account) {
1234 SOSAccountCleanupNotificationForAllPeers(account);
1235 SOSAccountUpdateOutOfSyncViews(account, NULL);
1236 }
1237
1238 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account) {
1239 bool hasBeenInSync = false;
1240
1241 if (!SOSAccountIsInCircle(account, NULL)) {
1242 SOSAccountCancelSyncChecking(account);
1243 } else {
1244 hasBeenInSync = SOSAccountHasBeenInSync(account);
1245 if (!hasBeenInSync) {
1246 hasBeenInSync = SOSAccountUpdateOutOfSyncViews(account, NULL);
1247 if (hasBeenInSync) {
1248 // Cancel and declare victory
1249 SOSAccountCancelSyncChecking(account);
1250 } else {
1251 // Make sure we're watching in case this is the fist attempt
1252 SOSAccountEnsureSyncChecking(account);
1253 }
1254 }
1255 }
1256
1257 return hasBeenInSync;
1258 }
1259
1260 //
1261 // MARK: Joining
1262 //
1263
1264 static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
1265 bool use_cloud_peer, CFErrorRef* error) {
1266 __block bool result = false;
1267 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1268
1269 require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1270 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1271
1272 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1273
1274 if (use_cloud_peer) {
1275 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1276 } else {
1277 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1278 }
1279
1280 if (SOSCircleCountPeers(account->trusted_circle) == 0) {
1281 result = SOSAccountResetCircleToOffering(account, user_key, error);
1282 } else {
1283 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1284 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1285 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1286 account->departure_code = kSOSNeverLeftCircle;
1287 if(result && cloud_full_peer) {
1288 CFErrorRef localError = NULL;
1289 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1290 require_quiet(cloudid, finish);
1291 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1292 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1293 finish:
1294 if (localError){
1295 secerror("Failed to join with cloud identity: %@", localError);
1296 CFReleaseNull(localError);
1297 }
1298 }
1299 return result;
1300 });
1301 }
1302
1303 fail:
1304 CFReleaseNull(cloud_full_peer);
1305 return result;
1306 }
1307
1308 static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
1309 bool success = false;
1310
1311 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1312 require_quiet(user_key, done); // Fail if we don't get one.
1313
1314 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1315
1316 if (account->my_identity != NULL) {
1317 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1318 success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
1319 require_quiet(!success, done);
1320
1321 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1322
1323 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1324 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1325
1326 CFReleaseNull(account->my_identity);
1327 myPeer = NULL;
1328 }
1329 }
1330
1331 success = SOSAccountJoinCircle(account, user_key, use_cloud_identity, error);
1332
1333 require_quiet(success, done);
1334
1335 account->departure_code = kSOSNeverLeftCircle;
1336
1337 done:
1338 return success;
1339 }
1340
1341 bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
1342 return SOSAccountJoinCircles_internal(account, false, error);
1343 }
1344
1345 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1346 CFStringRef result = NULL;
1347
1348 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1349
1350 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1351
1352 fail:
1353 return result;
1354 }
1355
1356 bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
1357 bool result = true;
1358
1359 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture){
1360 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1361 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1362 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1363
1364 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1365
1366 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1367 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDS, error);
1368 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1369
1370 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1371 });
1372 }
1373 else
1374 result = false;
1375 }
1376 else{
1377 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1378 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1379 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1380
1381 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1382
1383 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1384 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeKVS, error);
1385 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1386
1387 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1388 });
1389 }
1390 else
1391 result = false;
1392
1393 }
1394
1395 SOSCCSyncWithAllPeers();
1396
1397 fail:
1398 return result;
1399 }
1400
1401
1402 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1403 bool result = true;
1404 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1405 //construct message dictionary, circle -> peerID -> message
1406
1407 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1408 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1409
1410 char *messageCharStar;
1411 asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
1412 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1413
1414 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
1415
1416 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1417 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
1418 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1419 });
1420
1421 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1422 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1423
1424 CFReleaseNull(mutableDictionary);
1425 CFReleaseNull(peerToMessage);
1426 CFReleaseNull(circleToPeerMessages);
1427 CFReleaseNull(messageString);
1428 free(messageCharStar);
1429 }
1430 return result;
1431 }
1432
1433 bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1434 bool result = false;
1435 //construct message dictionary, circle -> peerID -> message
1436
1437 if(account->ids_message_transport == NULL)
1438 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1439
1440 require_quiet(account->ids_message_transport, fail);
1441 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1442 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1443
1444 char *messageCharStar;
1445 asprintf(&messageCharStar, "%d", kIDSStartPingTestMessage);
1446 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1447
1448 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
1449
1450 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1451 if(CFStringCompare(SOSAccountGetMyPeerID(account), SOSPeerInfoGetPeerID(peer), 0) != 0)
1452 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1453 });
1454
1455 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1456 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1457
1458 CFReleaseNull(mutableDictionary);
1459 CFReleaseNull(peerToMessage);
1460 CFReleaseNull(circleToPeerMessages);
1461 CFReleaseNull(messageString);
1462 free(messageCharStar);
1463 fail:
1464 return result;
1465 }
1466
1467 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
1468 bool result = true;
1469 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1470
1471 __block bool success = true;
1472 __block CFErrorRef localError = NULL;
1473 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1474 dispatch_retain(wait_for); // Both this scope and the block own it
1475
1476 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1477 success = (sync_error == NULL);
1478 if (!success) {
1479 CFRetainAssign(localError, sync_error);
1480 }
1481
1482 dispatch_semaphore_signal(wait_for);
1483 dispatch_release(wait_for);
1484 });
1485
1486 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1487 dispatch_release(wait_for);
1488
1489 if(!success && localError != NULL && error != NULL){
1490 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
1491 *error = localError;
1492 }
1493 else{
1494 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1495 }
1496 }
1497 return result;
1498 }
1499
1500 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
1501 return SOSAccountJoinCircles_internal(account, true, error);
1502 }
1503
1504
1505 bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1506 {
1507 bool result = true;
1508
1509 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1510 return sosAccountLeaveCircle(account, circle, error);
1511 });
1512
1513 account->departure_code = kSOSWithdrewMembership;
1514
1515 return result;
1516 }
1517
1518 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CFErrorRef* error)
1519 {
1520 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1521 if (!user_key)
1522 return false;
1523
1524 bool result = true;
1525
1526 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1527
1528 bool leaveCircle = CFSetContainsValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1529
1530 CFSetRemoveValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1531
1532 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1533 bool success = false;
1534
1535 require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
1536
1537 if (leaveCircle) {
1538 success = sosAccountLeaveCircle(account, circle, error);
1539 } else {
1540 success = SOSAccountGenerationSignatureUpdate(account, error);
1541 }
1542
1543 done:
1544 return success;
1545
1546 });
1547
1548 return result;
1549 }
1550
1551
1552 bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
1553 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1554 dispatch_group_t group = dispatch_group_create();
1555 __block bool result = false;
1556 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1557 // Add a task to the group
1558 dispatch_group_async(group, queue, ^{
1559 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1560 return sosAccountLeaveCircle(account, circle, error);
1561 });
1562 });
1563 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1564 dispatch_group_wait(group, milestone);
1565
1566 account->departure_code = kSOSWithdrewMembership;
1567
1568 dispatch_release(group);
1569 return result;
1570 }
1571
1572
1573 //
1574 // MARK: Application
1575 //
1576
1577 static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
1578 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1579 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1580 CFErrorRef peer_error = NULL;
1581 if (account->trusted_circle && me &&
1582 SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
1583 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
1584 __block bool modified = false;
1585 CFArrayForEach(peer_infos, ^(const void *value) {
1586 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1587 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1588 if (action(circle, account->my_identity, peer)) {
1589 modified = true;
1590 }
1591 }
1592 });
1593 return modified;
1594 });
1595 }
1596 if (peer_error)
1597 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1598 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1599 }
1600
1601 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1602 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1603 if (!user_key)
1604 return false;
1605
1606 __block bool success = true;
1607 __block int64_t num_peers = 0;
1608
1609 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1610 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1611 if (!accepted)
1612 success = false;
1613 else
1614 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1615 return accepted;
1616 });
1617
1618 return success;
1619 }
1620
1621 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1622 __block bool success = true;
1623 __block int64_t num_peers = 0;
1624
1625 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1626 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1627 if (!rejected)
1628 success = false;
1629 else
1630 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1631 return rejected;
1632 });
1633
1634 return success;
1635 }
1636
1637
1638 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1639 return CFSTR("We're compatible, go away");
1640 }
1641
1642 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1643 return account->departure_code;
1644 }
1645
1646 void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
1647 account->departure_code = reason;
1648 }
1649
1650
1651 CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
1652 CFArrayRef result = NULL;
1653 CFNumberRef generation = NULL;
1654
1655 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1656 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1657
1658 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
1659 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1660
1661 fail:
1662 return result;
1663 }
1664
1665 bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
1666 if (!SOSAccountHasPublicKey(account, error))
1667 return NULL;
1668
1669 return account->user_public_trusted;
1670 }
1671
1672 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
1673 // TODO: this result is never set or used
1674 bool result = true;
1675
1676 secnotice("updates", "Ensuring peer registration.");
1677
1678 require_quiet(account->trusted_circle, done);
1679 require_quiet(account->my_identity, done);
1680 // If we are not in the circle, there is no point in setting up peers
1681 require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
1682
1683 // This code only uses the SOSFullPeerInfoRef for two things:
1684 // - Finding out if this device is in the trusted circle
1685 // - Using the peerID for this device to see if the current peer is "me"
1686 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1687
1688 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1689
1690 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1691 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1692 CFErrorRef localError = NULL;
1693 SOSTransportMessageRef messageTransport = NULL;
1694
1695 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1696 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
1697 }
1698 else
1699 messageTransport = account->kvs_message_transport;
1700
1701 SOSPeerCoderInitializeForPeer(messageTransport->engine, account->my_identity, peer, &localError);
1702 if (localError)
1703 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
1704 CFReleaseSafe(localError);
1705 }
1706 });
1707
1708 //Initialize our device ID
1709 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1710 SOSTransportMessageIDSGetIDSDeviceID(account);
1711 }
1712
1713 done:
1714 return result;
1715 }
1716
1717 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
1718 if (!account->expansion) {
1719 account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
1720 }
1721
1722 return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
1723 }
1724
1725 bool SOSAccountClearValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1726 bool success = SOSAccountEnsureExpansion(account, error);
1727 require_quiet(success, errOut);
1728
1729 CFDictionaryRemoveValue(account->expansion, key);
1730 errOut:
1731 return success;
1732 }
1733
1734 bool SOSAccountSetValue(SOSAccountRef account, const void *key, const void *value, CFErrorRef *error) {
1735 bool success = SOSAccountEnsureExpansion(account, error);
1736 require_quiet(success, errOut);
1737
1738 CFDictionarySetValue(account->expansion, key, value);
1739 errOut:
1740 return success;
1741 }
1742
1743
1744 const void *SOSAccountGetValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1745 if (!account->expansion) {
1746 return NULL;
1747 }
1748 return CFDictionaryGetValue(account->expansion, key);
1749 }
1750
1751 bool SOSAccountAddEscrowRecords(SOSAccountRef account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1752 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1753 CFMutableDictionaryRef escrowCopied = NULL;
1754 bool success = false;
1755
1756 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1757 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1758 else
1759 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1760
1761 CFDictionaryAddValue(escrowCopied, dsid, record);
1762 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1763
1764 if(*error == NULL)
1765 success = true;
1766
1767 CFReleaseNull(escrowCopied);
1768
1769 return success;
1770
1771 }
1772
1773 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1774 bool success = false;
1775
1776 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1777 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1778
1779 return success;
1780 }
1781
1782 bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
1783 {
1784 CFMutableDictionaryRef circleToPeerMessages = NULL;
1785 CFStringRef messageString = NULL;
1786 CFMutableDictionaryRef mutableDictionary = NULL;
1787 CFMutableSetRef peers = NULL;
1788 CFMutableDictionaryRef peerList = NULL;
1789 char* message = NULL;
1790 bool result = false;
1791 if(account->ids_message_transport == NULL)
1792 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1793
1794 require_quiet(account->ids_message_transport, fail);
1795 circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1796
1797 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
1798
1799 asprintf(&message, "%d", kIDSPeerAvailability);
1800 messageString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
1801
1802 mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("checking peers"), NULL);
1803
1804 //make sure there are peers in the circle
1805 peers = SOSCircleCopyPeers(account->trusted_circle, kCFAllocatorDefault);
1806 require_quiet(CFSetGetCount(peers) > 0, fail);
1807 CFReleaseNull(peers);
1808
1809 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1810 SOSCircleRef circle = account->trusted_circle;
1811
1812 //check each peer to make sure they have the right view set enabled
1813 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1814 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
1815 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account))){
1816 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1817 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1818 if(CFEqualSafe(intersectSets, mySubSet)){
1819 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1820 if(deviceID != NULL)
1821 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1822 CFReleaseNull(deviceID);
1823 }
1824 CFReleaseNull(peerViews);
1825 CFReleaseNull(intersectSets);
1826 }
1827 });
1828
1829 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1830 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerList);
1831 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1832
1833 fail:
1834 CFReleaseNull(mutableDictionary);
1835 CFReleaseNull(messageString);
1836 CFReleaseNull(peerList);
1837 CFReleaseNull(circleToPeerMessages);
1838 CFReleaseNull(peers);
1839 free(message);
1840 return result;
1841 }
1842
1843
1844 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
1845 if (!SOSAccountIsInCircle(account, NULL))
1846 return;
1847
1848 SOSAccountModifyCircle(account, NULL, ^bool (SOSCircleRef circle) {
1849 __block bool updated = false;
1850 CFSetForEach(account->retirees, ^(CFTypeRef element){
1851 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1852
1853 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1854 updated = true;
1855 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1856 CFErrorRef cleanupError = NULL;
1857 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
1858 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1859 CFReleaseSafe(cleanupError);
1860 }
1861 });
1862 return updated;
1863 });
1864 }
1865
1866 void SOSAccountFinishTransaction(SOSAccountRef account) {
1867 if(account->circle_rings_retirements_need_attention){
1868 SOSAccountRecordRetiredPeersInCircle(account);
1869
1870 CFErrorRef localError = NULL;
1871 if(!SOSTransportCircleFlushChanges(account->circle_transport, &localError)) {
1872 secerror("flush circle failed %@", localError);
1873 }
1874 CFReleaseSafe(localError);
1875
1876 SOSAccountNotifyEngines(account); // For now our only rings are backup rings.
1877 }
1878
1879 SOSAccountCheckHasBeenInSync(account);
1880
1881 account->circle_rings_retirements_need_attention = false;
1882 }
1883