]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57337.50.23.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 #define DATE_LENGTH 25
39 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
40
41
42 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
43 {
44 bool result = false;
45 CFStringRef circle_name = NULL;
46
47 require_quiet(a, xit);
48 require_quiet(a->factory, xit);
49
50 circle_name = SOSDataSourceFactoryCopyName(a->factory);
51 require(circle_name, xit);
52
53 SOSAccountEnsureCircle(a, circle_name, NULL);
54
55 result = true;
56
57 xit:
58 // We don't own name, so don't release it.
59 CFReleaseNull(circle_name);
60 return result;
61 }
62
63
64 SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
65 CFDictionaryRef gestalt,
66 SOSDataSourceFactoryRef factory) {
67 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
68
69 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
70
71 a->notification_cleanups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
72
73 a->gestalt = CFRetainSafe(gestalt);
74
75 a->trusted_circle = NULL;
76 a->trusted_rings = CFDictionaryCreateMutableForCFTypes(allocator);
77 a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
78 a->my_identity = NULL;
79 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
80
81 a->factory = factory; // We adopt the factory. kthanksbai.
82
83 a->_user_private = NULL;
84 a->_password_tmp = NULL;
85 a->user_private_timer = NULL;
86
87 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
88 a->waitForInitialSync_blocks = CFDictionaryCreateMutableForCFTypes(allocator);
89 a->departure_code = kSOSNeverAppliedToCircle;
90
91 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
92 a->circle_transport = NULL;
93 a->kvs_message_transport = NULL;
94 a->ids_message_transport = NULL;
95 a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
96
97 return a;
98 }
99
100 SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
101 SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
102 bool updateCircle = false;
103 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
104 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
105 retval = SOSFullPeerInfoUpdateSecurityProperty(account->my_identity, actionCode, property, error);
106
107 if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
108 updateCircle = true;
109 } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
110 updateCircle = true;
111 } else if(actionCode == kSOSCCSecurityPropertyPending) {
112 updateCircle = true;
113 }
114
115 if (updateCircle) {
116 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
117 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
118 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
119 });
120 }
121
122 errOut:
123 return retval;
124 }
125
126 SOSSecurityPropertyResultCode SOSAccountSecurityPropertyStatus(SOSAccountRef account, CFStringRef property, CFErrorRef *error) {
127 SOSSecurityPropertyResultCode retval = kSOSCCGeneralViewError;
128 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
129 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
130 retval = SOSFullPeerInfoSecurityPropertyStatus(account->my_identity, property, error);
131 errOut:
132 return retval;
133 }
134
135 bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
136 {
137 if (CFEqualSafe(new_gestalt, account->gestalt))
138 return false;
139
140 if (account->trusted_circle && account->my_identity
141 && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
142 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
143 secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
144 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
145 });
146 }
147
148 CFRetainAssign(account->gestalt, new_gestalt);
149 return true;
150 }
151
152 bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
153 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
154 //send new DSID over account changed
155 SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
156
157 return true;
158 }
159
160 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews, CFSetRef excludedViews) {
161 if (account->trusted_circle && account->my_identity) {
162 if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
163 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
164 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
165 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
166 });
167 }
168 }
169
170 return true;
171 }
172
173 SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
174 SOSViewResultCode retval = kSOSCCGeneralViewError;
175 SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
176 bool updateCircle = false;
177 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
178 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
179 require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
180 currentStatus = SOSAccountViewStatus(account, viewname, error);
181 require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
182
183 if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
184 // The V0 view switches on and off all on it's own, we allow people the delusion
185 // of control and status if it's what we're stuck at., otherwise error.
186 if (SOSAccountSyncingV0(account)) {
187 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
188 retval = kSOSCCViewMember;
189 } else {
190 require_action_quiet(actionCode = kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
191 retval = kSOSCCViewNotMember;
192 }
193 } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
194 // Subviews of V0 syncing can't be turned off if V0 is on.
195 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
196 retval = kSOSCCViewMember;
197 } else {
198 if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
199 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
200 if(retval == kSOSCCViewMember) updateCircle = true;
201 } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
202 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
203 if(retval == kSOSCCViewNotMember) updateCircle = true;
204 } else {
205 retval = currentStatus;
206 }
207
208 if (updateCircle) {
209 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
210 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
211 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
212 });
213 }
214 }
215
216 errOut:
217 return retval;
218 }
219
220 SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewname, CFErrorRef *error) {
221 SOSViewResultCode retval = kSOSCCGeneralViewError;
222 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
223 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
224
225 retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
226
227 // If that doesn't say we're a member and this view is a V0 subview, and we're syncing V0 views we are a member
228 if (retval != kSOSCCViewMember) {
229 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
230 && SOSAccountSyncingV0(account)) {
231 retval = kSOSCCViewMember;
232 }
233 }
234
235 // If we're only an applicant we report pending if we would be a view member
236 if (retval == kSOSCCViewMember) {
237 bool isApplicant = SOSCircleHasApplicant(account->trusted_circle, SOSAccountGetMyPeerInfo(account), error);
238 if (isApplicant) {
239 retval = kSOSCCViewPending;
240 }
241 }
242
243 errOut:
244 return retval;
245 }
246
247 static void dumpViewSet(CFStringRef label, CFSetRef views) {
248 if(views) {
249 secnotice("circleChange", "%@ list: %@", label, views);
250 } else {
251 secnotice("circleChange", "No %@ list provided.", label);
252 }
253 }
254
255 bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef enabledViews, CFSetRef disabledViews) {
256 bool updateCircle = false;
257 dumpViewSet(CFSTR("Enabled"), enabledViews);
258 dumpViewSet(CFSTR("Disabled"), disabledViews);
259
260 require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
261 require_action_quiet(account->my_identity, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
262 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
263
264 // Copy my views
265 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
266 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
267
268 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
269
270
271 if(!SOSPeerInfoVersionIsCurrent(pi)) {
272 if(!SOSPeerInfoUpdateToV2(pi, NULL)) {
273 secnotice("views", "Unable to update peer to V2- can't update views");
274 return false;
275 }
276 }
277
278 if(enabledViews) updateCircle = SOSViewSetEnable(pi, enabledViews);
279 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
280
281 /* UPDATE FULLPEERINFO VIEWS */
282
283 if (updateCircle && SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL)) {
284 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
285 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
286 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
287 });
288 }
289
290 errOut:
291 return updateCircle;
292 }
293
294
295 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
296 CFDictionaryRef gestalt,
297 SOSDataSourceFactoryRef factory) {
298 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
299
300 SOSAccountEnsureFactoryCircles(a);
301
302 SOSUpdateKeyInterest(a);
303
304 return a;
305 }
306
307 static void SOSAccountDestroy(CFTypeRef aObj) {
308 SOSAccountRef a = (SOSAccountRef) aObj;
309
310 // We don't own the factory, merely have a reference to the singleton
311 // Don't free it.
312 // a->factory
313
314 SOSAccountCleanupNotificationForAllPeers(a);
315
316 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
317
318 if (engine)
319 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
320
321 dispatch_sync(a->queue, ^{
322 CFReleaseNull(a->gestalt);
323
324 CFReleaseNull(a->my_identity);
325 CFReleaseNull(a->trusted_circle);
326 CFReleaseNull(a->trusted_rings);
327 CFReleaseNull(a->backups);
328 CFReleaseNull(a->retirees);
329
330 a->user_public_trusted = false;
331 CFReleaseNull(a->user_public);
332 CFReleaseNull(a->user_key_parameters);
333
334 SOSAccountPurgePrivateCredential(a);
335 CFReleaseNull(a->previous_public);
336 CFReleaseNull(a->_user_private);
337 CFReleaseNull(a->_password_tmp);
338
339 a->departure_code = kSOSNeverAppliedToCircle;
340 CFReleaseNull(a->kvs_message_transport);
341 CFReleaseNull(a->ids_message_transport);
342 CFReleaseNull(a->key_transport);
343 CFReleaseNull(a->circle_transport);
344 dispatch_release(a->queue);
345 CFReleaseNull(a->notification_cleanups);
346
347 dispatch_release(a->user_private_timer);
348 CFReleaseNull(a->change_blocks);
349 CFReleaseNull(a->waitForInitialSync_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 bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
587 {
588 bool result = true;
589 __block bool SyncingCompletedOverIDS = true;
590 __block bool SyncingCompletedOverKVS = true;
591 __block CFErrorRef localError = NULL;
592 SOSCircleRef circle = SOSAccountGetCircle(account, error);
593 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
594 CFMutableArrayRef peerIds = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
595
596 require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
597 SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
598 NULL, &localError));
599
600 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
601 if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
602 if (SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)) {
603 secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
604 CFMutableDictionaryRef circleToIdsId = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
605 CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
606 CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
607 CFDictionaryAddValue(circleToIdsId, SOSCircleGetName(circle), ids);
608 SyncingCompletedOverIDS = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToIdsId, &localError);
609 if(!SyncingCompletedOverIDS){
610 secerror("Failed to sync over IDS, falling back to KVS");
611 SyncingCompletedOverIDS = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToIdsId, &localError);
612 }
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 // Tell account to update SOSEngine with current trusted peers
640 if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
641 secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
642 account->circle_rings_retirements_need_attention = true;
643 }
644 CFErrorPropagate(localError, error);
645 localError = NULL;
646 }
647 CFReleaseNull(peerIds);
648 CFReleaseSafe(localError);
649 return result;
650 }
651
652 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
653 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
654 {
655 bool success = true;
656
657 SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
658 require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
659 require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
660
661 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
662
663 CFStringRef circle_name = SOSCircleGetName(circle);
664
665 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
666 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
667
668 CFErrorRef localError = NULL;
669 if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
670 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
671 }
672
673 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
674 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
675 }
676
677 CFReleaseNull(localError);
678
679 if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
680 if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
681 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
682 }
683 }
684 CFReleaseNull(localError);
685 CFReleaseNull(circleToPeerIDs);
686
687 xit:
688 return success;
689 }
690
691 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
692 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
693
694 __block bool success = true;
695
696 CFSetForEach(account->retirees, ^(const void *value) {
697 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
698
699 if (retiree) {
700 // Remove the entry if it's not a retired peer or if it's retirment ticket has expired AND he's no longer in the circle.
701 if (!SOSPeerInfoIsRetirementTicket(retiree) ||
702 (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
703 CFSetAddValue(retirees_to_remove, retiree);
704 };
705 }
706 });
707
708 CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
709 CFSetForEach(retirees_to_remove, ^(const void *value) {
710 CFArrayAppendValue(retirees_to_cleanup, value);
711 CFSetRemoveValue(account->retirees, value);
712 });
713
714 CFReleaseNull(retirees_to_remove);
715
716 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
717 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
718 NULL);
719
720 CFReleaseNull(retirees_to_cleanup);
721
722 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
723
724 CFReleaseNull(retirements_to_remove);
725
726 return success;
727 }
728
729 bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
730 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
731 CFSetSetValue(account->retirees, peer);
732 CFErrorRef cleanupError = NULL;
733 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
734 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
735 }
736 CFReleaseSafe(cleanupError);
737 });
738 return true;
739 }
740
741 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
742 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
743 if(!new_circle) return NULL;
744
745 if (account->retirees) {
746 CFSetForEach(account->retirees, ^(const void* value) {
747 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
748 if (isSOSPeerInfo(pi)) {
749 SOSCircleUpdatePeerInfo(new_circle, pi);
750 }
751 });
752 }
753
754 if(SOSCircleCountPeers(new_circle) == 0) {
755 SOSCircleResetToEmpty(new_circle, NULL);
756 }
757
758 return new_circle;
759 }
760
761 //
762 // MARK: Circle Membership change notificaion
763 //
764
765 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
766 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
767 CFArrayAppendValue(a->change_blocks, copy);
768 CFReleaseNull(copy);
769 }
770
771 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
772 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
773 }
774
775 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
776 if (!changeBlock) return;
777
778 CFRetainSafe(ds_name);
779 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
780 CFSetRef added_peers, CFSetRef removed_peers,
781 CFSetRef added_applicants, CFSetRef removed_applicants) {
782
783 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
784 return;
785
786 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
787 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
788
789 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
790 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
791 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
792
793 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
794 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
795 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
796 });
797
798 CFSetForEach(added_peers, ^(const void *value) {
799 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
800 });
801
802 CFSetForEach(removed_peers, ^(const void *value) {
803 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
804 });
805 }
806
807 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
808 changeBlock(peer_ids, added_ids, removed_ids);
809
810 CFReleaseSafe(peer_ids);
811 CFReleaseSafe(added_ids);
812 CFReleaseSafe(removed_ids);
813 };
814
815 CFRetainSafe(changeBlock);
816 SOSAccountAddChangeBlock(a, block_to_register);
817
818 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
819 if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
820 block_to_register(a->trusted_circle, empty, empty, empty, empty);
821 }
822 CFReleaseSafe(empty);
823 }
824
825 void SOSAccountPurgeIdentity(SOSAccountRef account) {
826 if (account->my_identity) {
827 // Purge private key but don't return error if we can't.
828 CFErrorRef purgeError = NULL;
829 if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
830 secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
831 }
832 CFReleaseNull(purgeError);
833
834 CFReleaseNull(account->my_identity);
835 }
836 }
837
838 bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
839 SOSFullPeerInfoRef fpi = account->my_identity;
840 if(!fpi) return false;
841
842 CFErrorRef localError = NULL;
843
844 bool retval = false;
845
846 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
847 if (!retire_peer) {
848 secerror("Create ticket failed for peer %@: %@", fpi, localError);
849 } else {
850 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
851 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
852 // Remove our application if we have one.
853 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
854 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
855 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
856 CFErrorRef cleanupError = NULL;
857 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
858 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
859 }
860 CFReleaseSafe(cleanupError);
861 }
862 }
863
864 // Store the retirement record locally.
865 CFSetAddValue(account->retirees, retire_peer);
866
867 // Write retirement to Transport
868 CFErrorRef postError = NULL;
869 if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
870 secwarning("Couldn't post retirement (%@)", postError);
871 }
872 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
873 secwarning("Couldn't flush retirement data (%@)", postError);
874 }
875 CFReleaseNull(postError);
876 }
877
878 SOSAccountPurgeIdentity(account);
879
880 retval = true;
881
882 CFReleaseNull(localError);
883 CFReleaseNull(retire_peer);
884 return retval;
885 }
886
887 bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
888 SOSFullPeerInfoRef fpi = account->my_identity;
889 if(!fpi) return false;
890 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
891 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
892
893 CFErrorRef localError = NULL;
894
895 bool retval = false;
896 bool writeRing = false;
897 bool writePeerInfo = false;
898
899 if(SOSRingHasPeerID(ring, peerID)) {
900 writePeerInfo = true;
901 }
902
903 #if 0
904 // this was circle behavior - at some point
905 if(SOSRingHasApplicant(ring, peerID)) {
906 writeRing = true;
907 }
908 #endif
909
910 if(writePeerInfo || writeRing) {
911 SOSRingWithdraw(ring, NULL, fpi, error);
912 }
913
914 // Write leave thing to Transport
915 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
916 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
917
918 if (writeRing) {
919 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
920
921 if (ring_data) {
922 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
923 }
924 CFReleaseNull(ring_data);
925 }
926 retval = true;
927 CFReleaseNull(localError);
928 return retval;
929 }
930
931 bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
932 bool result = false;
933 SOSTransportCircleRef transport = account->circle_transport;
934 if (transport) {
935 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
936 }
937 return result;
938 }
939
940 /*
941 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
942 local value that has been overwritten by a distant value. If there is no
943 conflict between the local and the distant values when doing the initial
944 sync (e.g. if the cloud has no data stored or the client has not stored
945 any data yet), you'll never see that notification.
946
947 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
948 with server but initial round trip with server does not imply
949 NSUbiquitousKeyValueStoreInitialSyncChange.
950 */
951
952
953 //
954 // MARK: Status summary
955 //
956
957 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
958 if (!circle)
959 return kSOSCCNotInCircle;
960
961 if (circle && SOSCircleCountPeers(circle) == 0)
962 return kSOSCCCircleAbsent;
963
964 if (this_peer) {
965
966 if(SOSPeerInfoIsRetirementTicket(this_peer))
967 return kSOSCCNotInCircle;
968
969 if (SOSCircleHasPeer(circle, this_peer, NULL))
970 return kSOSCCInCircle;
971
972 if (SOSCircleHasApplicant(circle, this_peer, NULL))
973 return kSOSCCRequestPending;
974 }
975
976 return kSOSCCNotInCircle;
977 }
978
979 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
980 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
981
982 if (result != kSOSCCInCircle && result != kSOSCCError) {
983 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
984 return false;
985 }
986
987 return true;
988 }
989
990 SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
991 if (!SOSAccountHasPublicKey(account, error)) {
992 return kSOSCCError;
993 }
994
995 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
996 }
997
998 //
999 // MARK: Account Reset Circles
1000 //
1001
1002 static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef user_key, CFErrorRef *error) {
1003 bool result = false;
1004
1005 require(SOSAccountHasCircle(account, error), fail);
1006 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1007
1008 (void) SOSAccountResetAllRings(account, error);
1009
1010 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1011 bool result = false;
1012 SOSFullPeerInfoRef cloud_identity = NULL;
1013 CFErrorRef localError = NULL;
1014
1015 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1016
1017 {
1018 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
1019 require_quiet(cloud_peer, err_out);
1020 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
1021 CFReleaseNull(cloud_peer);
1022 require_quiet(cloud_identity, err_out);
1023 }
1024
1025 account->departure_code = kSOSNeverLeftCircle;
1026 require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
1027 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
1028 require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
1029 result = true;
1030 SOSAccountPublishCloudParameters(account, NULL);
1031
1032 err_out:
1033 if (result == false)
1034 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1035 if (localError && error && *error == NULL) {
1036 *error = localError;
1037 localError = NULL;
1038 }
1039 CFReleaseNull(localError);
1040 CFReleaseNull(cloud_identity);
1041 return result;
1042 });
1043
1044 result = true;
1045
1046 fail:
1047 return result;
1048 }
1049
1050
1051 bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
1052 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1053 if (!user_key)
1054 return false;
1055
1056 CFReleaseNull(account->my_identity);
1057
1058 return user_key && SOSAccountResetCircleToOffering(account, user_key, error);
1059 }
1060
1061 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1062 if (!SOSAccountHasPublicKey(account, error))
1063 return false;
1064 __block bool result = true;
1065
1066 result &= SOSAccountResetAllRings(account, error);
1067
1068 CFReleaseNull(account->my_identity);
1069
1070 account->departure_code = kSOSWithdrewMembership;
1071 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1072 result = SOSCircleResetToEmpty(circle, error);
1073 return result;
1074 });
1075
1076 if (!result) {
1077 secerror("error: %@", error ? *error : NULL);
1078 }
1079
1080 return result;
1081 }
1082 //
1083 // MARK: start backups
1084 //
1085
1086 bool SOSAccountEnsureBackupStarts(SOSAccountRef account) {
1087
1088 __block bool result = false;
1089 __block CFErrorRef error = NULL;
1090 secnotice("backup", "Starting new backups");
1091
1092 CFDataRef backupKey = SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey);
1093
1094 if (CFEqualSafe(backupKey, account->backup_key)){
1095 CFReleaseNull(backupKey);
1096 return true;
1097 }
1098
1099 if(account->backup_key != NULL){
1100 require_quiet(SOSBSKBIsGoodBackupPublic(account->backup_key, &error), exit);
1101 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error,
1102 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1103 return SOSFullPeerInfoUpdateBackupKey(fpi, account->backup_key, error);
1104 }), exit);
1105 CFErrorRef localError = NULL;
1106 if (!SOSDeleteV0Keybag(&localError)) {
1107 secerror("Failed to delete v0 keybag: %@", localError);
1108 }
1109 CFReleaseNull(localError);
1110
1111 result = true;
1112
1113 SOSAccountForEachBackupView(account, ^(const void *value) {
1114 CFStringRef viewName = (CFStringRef)value;
1115 result &= SOSAccountStartNewBackup(account, viewName, &error);
1116 });
1117 }
1118 else{
1119 if(account->backup_key == NULL){
1120 secerror("account backup key is NULL!");
1121 }
1122 }
1123
1124 exit:
1125 if (!result) {
1126 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1127 }
1128 CFReleaseNull(backupKey);
1129 return result;
1130 }
1131
1132 //
1133 // MARK: Waiting for in-sync
1134 //
1135
1136 static bool SOSAccountHasBeenInSync(SOSAccountRef account) {
1137 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1138 CFSetRef unsynced = asSet(unsyncedObject, NULL);
1139
1140 return !(unsyncedObject == kCFBooleanTrue || (unsynced && (CFSetGetCount(unsynced) > 0)));
1141 }
1142
1143 static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account, CFSetRef viewsInSync) {
1144 bool notifyOfChange = false;
1145
1146 SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
1147 bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
1148
1149 CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
1150
1151 if (!inOrApplying) {
1152 if (unsyncedObject != NULL) {
1153 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1154 secnotice("initial-sync", "in sync, clearing pending");
1155 notifyOfChange = true;
1156 }
1157 } else if (circleStatus == kSOSCCInCircle) {
1158 __block CFMutableSetRef viewsToSync = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1159 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1160 SOSPeerInfoWithEnabledViewSet(peer, ^(CFSetRef enabled) {
1161 CFSetUnion(viewsToSync, enabled);
1162 });
1163 });
1164
1165 if (viewsInSync) {
1166 CFSetSubtract(viewsToSync, viewsInSync);
1167
1168 }
1169
1170 if (unsyncedObject == kCFBooleanTrue) {
1171 if (CFSetGetCount(viewsToSync) == 0) {
1172 secnotice("initial-sync", "No views to wait for");
1173 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1174 } else {
1175 __block CFSetRef newViews = NULL;
1176 SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account), ^(CFSetRef enabled) {
1177 newViews = CFSetCreateIntersection(kCFAllocatorDefault, enabled, viewsToSync);
1178 });
1179 secnotice("initial-sync", "Pending views set from True: %@", newViews);
1180 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
1181 CFReleaseNull(newViews);
1182 }
1183 notifyOfChange = true;
1184 } else if (isSet(unsyncedObject)) {
1185 CFSetRef waiting = (CFMutableSetRef) unsyncedObject;
1186 CFSetRef newViews = CFSetCreateIntersection(kCFAllocatorDefault, waiting, viewsToSync);
1187 if (!CFEqualSafe(waiting, newViews)) {
1188 if (CFSetGetCount(newViews) == 0) {
1189 secnotice("initial-sync", "No views left to wait for.");
1190 SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
1191 } else {
1192 secnotice("initial-sync", "Pending views updated: %@", newViews);
1193 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
1194 }
1195 notifyOfChange = true;
1196 }
1197 CFReleaseNull(newViews);
1198 }
1199
1200 CFReleaseNull(viewsToSync);
1201 }
1202
1203 if (notifyOfChange) {
1204 if(SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL) == NULL){
1205 CFDictionaryRef syncBlocks = account->waitForInitialSync_blocks;
1206 account->waitForInitialSync_blocks = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1207
1208 CFDictionaryForEach(syncBlocks, ^(const void *key, const void *value) {
1209 secnotice("updates", "calling in sync block [%@]", key);
1210 ((SOSAccountWaitForInitialSyncBlock)value)(account);
1211 });
1212
1213 CFReleaseNull(syncBlocks);
1214 }
1215
1216 // Make sure we update the engine
1217 account->circle_rings_retirements_need_attention = true;
1218 }
1219
1220 return SOSAccountHasBeenInSync(account);
1221 }
1222
1223 static void SOSAccountPeerGotInSync(SOSAccountRef account, CFStringRef peerID) {
1224 secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID);
1225
1226 if (account->trusted_circle) {
1227 SOSPeerInfoRef peer = SOSCircleCopyPeerWithID(account->trusted_circle, peerID, NULL);
1228 if (peer) {
1229 CFSetRef views = SOSPeerInfoCopyEnabledViews(peer);
1230 SOSAccountUpdateOutOfSyncViews(account, views);
1231 CFReleaseNull(views);
1232 }
1233 CFReleaseNull(peer);
1234 }
1235 }
1236
1237 void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account) {
1238 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1239
1240 CFDictionaryForEach(account->notification_cleanups, ^(const void *key, const void *value) {
1241 if (engine) {
1242 SOSEngineSetSyncCompleteListener(engine, key, NULL);
1243 }
1244 dispatch_async(account->queue, value);
1245 });
1246
1247 CFDictionaryRemoveAllValues(account->notification_cleanups);
1248 }
1249
1250 static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account, CFStringRef peerID) {
1251 dispatch_block_t cleanup = CFDictionaryGetValue(account->notification_cleanups, peerID);
1252
1253 if (cleanup) {
1254 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1255
1256 if (engine) {
1257 SOSEngineSetSyncCompleteListener(engine, peerID, NULL);
1258 }
1259
1260 dispatch_async(account->queue, cleanup);
1261 }
1262
1263 CFDictionaryRemoveValue(account->notification_cleanups, peerID);
1264
1265 }
1266
1267 static void SOSAccountRegisterCleanupBlock(SOSAccountRef account, CFStringRef peerID, dispatch_block_t block) {
1268 dispatch_block_t copy = Block_copy(block);
1269 CFDictionarySetValue(account->notification_cleanups, peerID, copy);
1270 CFReleaseNull(copy);
1271 }
1272
1273 void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
1274 if (CFDictionaryGetCount(account->notification_cleanups) == 0) {
1275 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
1276 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
1277
1278 SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
1279
1280 if (engine) {
1281 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1282 CFStringRef peerID = CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerID(peer));
1283
1284 secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID);
1285 SOSAccountRegisterCleanupBlock(account, peerID, ^{
1286 CFReleaseSafe(peerID);
1287 });
1288
1289 SOSEngineSetSyncCompleteListener(engine, peerID, ^{
1290 SOSAccountPeerGotInSync(account, peerID);
1291 SOSAccountCleanupNotificationForPeer(account, peerID);
1292 SOSAccountFinishTransaction(account);
1293 });
1294 });
1295 } else {
1296 secerror("Couldn't find engine to setup notifications!!!");
1297 }
1298 }
1299 }
1300
1301 void SOSAccountCancelSyncChecking(SOSAccountRef account) {
1302 SOSAccountCleanupNotificationForAllPeers(account);
1303 SOSAccountUpdateOutOfSyncViews(account, NULL);
1304 }
1305
1306 bool SOSAccountCheckHasBeenInSync(SOSAccountRef account) {
1307 bool hasBeenInSync = false;
1308
1309 if (!SOSAccountIsInCircle(account, NULL)) {
1310 SOSAccountCancelSyncChecking(account);
1311 } else {
1312 hasBeenInSync = SOSAccountHasBeenInSync(account);
1313 if (!hasBeenInSync) {
1314 hasBeenInSync = SOSAccountUpdateOutOfSyncViews(account, NULL);
1315 if (hasBeenInSync) {
1316 // Cancel and declare victory
1317
1318 SOSAccountCancelSyncChecking(account);
1319 } else {
1320 // Make sure we're watching in case this is the fist attempt
1321 SOSAccountEnsureSyncChecking(account);
1322 }
1323 }
1324 }
1325
1326 return hasBeenInSync;
1327 }
1328
1329 //
1330 // MARK: Joining
1331 //
1332
1333 static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
1334 bool use_cloud_peer, CFErrorRef* error) {
1335 __block bool result = false;
1336 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1337
1338 require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1339 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1340
1341 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1342
1343 if (use_cloud_peer) {
1344 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1345 } else {
1346 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1347 }
1348
1349 if (SOSCircleCountPeers(account->trusted_circle) == 0) {
1350 result = SOSAccountResetCircleToOffering(account, user_key, error);
1351 } else {
1352 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1353 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1354 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1355 account->departure_code = kSOSNeverLeftCircle;
1356 if(result && cloud_full_peer) {
1357 CFErrorRef localError = NULL;
1358 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1359 require_quiet(cloudid, finish);
1360 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1361 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1362 finish:
1363 if (localError){
1364 secerror("Failed to join with cloud identity: %@", localError);
1365 CFReleaseNull(localError);
1366 }
1367 }
1368 return result;
1369 });
1370 }
1371
1372 fail:
1373 CFReleaseNull(cloud_full_peer);
1374 return result;
1375 }
1376
1377 static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
1378 bool success = false;
1379
1380 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1381 require_quiet(user_key, done); // Fail if we don't get one.
1382
1383 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1384
1385 if (account->my_identity != NULL) {
1386 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1387 success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
1388 require_quiet(!success, done);
1389
1390 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1391
1392 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1393 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1394
1395 CFReleaseNull(account->my_identity);
1396 myPeer = NULL;
1397 }
1398 }
1399
1400 success = SOSAccountJoinCircle(account, user_key, use_cloud_identity, error);
1401
1402 require_quiet(success, done);
1403
1404 account->departure_code = kSOSNeverLeftCircle;
1405
1406 done:
1407 return success;
1408 }
1409
1410 bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
1411 return SOSAccountJoinCircles_internal(account, false, error);
1412 }
1413
1414 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1415 CFStringRef result = NULL;
1416
1417 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1418
1419 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1420
1421 fail:
1422 return result;
1423 }
1424
1425 bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
1426 bool result = true;
1427
1428 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture){
1429 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1430 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1431 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1432
1433 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1434
1435 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1436 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDS, error);
1437 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1438
1439 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1440 });
1441 }
1442 else
1443 result = false;
1444 }
1445 else{
1446 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1447 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1448 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1449
1450 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1451
1452 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1453 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeKVS, error);
1454 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
1455
1456 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1457 });
1458 }
1459 else
1460 result = false;
1461
1462 }
1463
1464 SOSCCSyncWithAllPeers();
1465
1466 fail:
1467 return result;
1468 }
1469
1470
1471 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1472 bool result = true;
1473 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1474 //construct message dictionary, circle -> peerID -> message
1475
1476 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1477 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1478
1479 char *messageCharStar;
1480 asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
1481 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1482
1483 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
1484
1485 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1486 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
1487 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1488 });
1489
1490 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1491 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1492
1493 CFReleaseNull(mutableDictionary);
1494 CFReleaseNull(peerToMessage);
1495 CFReleaseNull(circleToPeerMessages);
1496 CFReleaseNull(messageString);
1497 free(messageCharStar);
1498 }
1499 return result;
1500 }
1501
1502 bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1503 bool result = false;
1504 //construct message dictionary, circle -> peerID -> message
1505
1506 if(account->ids_message_transport == NULL)
1507 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1508
1509 require_quiet(account->ids_message_transport, fail);
1510 CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1511 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1512
1513 char *messageCharStar;
1514 asprintf(&messageCharStar, "%d", kIDSStartPingTestMessage);
1515 CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
1516
1517 CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
1518
1519 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1520 if(CFStringCompare(SOSAccountGetMyPeerID(account), SOSPeerInfoGetPeerID(peer), 0) != 0)
1521 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1522 });
1523
1524 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
1525 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1526
1527 CFReleaseNull(mutableDictionary);
1528 CFReleaseNull(peerToMessage);
1529 CFReleaseNull(circleToPeerMessages);
1530 CFReleaseNull(messageString);
1531 free(messageCharStar);
1532 fail:
1533 return result;
1534 }
1535
1536 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
1537 bool result = true;
1538 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1539
1540 __block bool success = true;
1541 __block CFErrorRef localError = NULL;
1542 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1543 dispatch_retain(wait_for); // Both this scope and the block own it
1544
1545 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1546 success = (sync_error == NULL);
1547 if (!success) {
1548 CFRetainAssign(localError, sync_error);
1549 }
1550
1551 dispatch_semaphore_signal(wait_for);
1552 dispatch_release(wait_for);
1553 });
1554
1555 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1556 dispatch_release(wait_for);
1557
1558 if(!success && localError != NULL && error != NULL){
1559 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
1560 *error = localError;
1561 }
1562 else{
1563 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1564 }
1565 }
1566 return result;
1567 }
1568
1569 bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
1570 return SOSAccountJoinCircles_internal(account, true, error);
1571 }
1572
1573
1574 bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1575 {
1576 bool result = true;
1577
1578 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1579 return sosAccountLeaveCircle(account, circle, error);
1580 });
1581
1582 account->departure_code = kSOSWithdrewMembership;
1583
1584 return result;
1585 }
1586
1587 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CFErrorRef* error)
1588 {
1589 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1590 if (!user_key)
1591 return false;
1592
1593 bool result = true;
1594
1595 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1596
1597 bool leaveCircle = CFSetContainsValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1598
1599 CFSetRemoveValue(peersToRemove, SOSAccountGetMyPeerInfo(account));
1600
1601 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1602 bool success = false;
1603
1604 if(CFSetGetCount(peersToRemove) != 0) {
1605 require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
1606 success = SOSAccountGenerationSignatureUpdate(account, error);
1607 } else success = true;
1608
1609 if (success && leaveCircle) {
1610 success = sosAccountLeaveCircle(account, circle, error);
1611 }
1612
1613 done:
1614 return success;
1615
1616 });
1617
1618 return result;
1619 }
1620
1621
1622 bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
1623 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1624 dispatch_group_t group = dispatch_group_create();
1625 __block bool result = false;
1626 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1627 // Add a task to the group
1628 dispatch_group_async(group, queue, ^{
1629 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1630 return sosAccountLeaveCircle(account, circle, error);
1631 });
1632 });
1633 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1634 dispatch_group_wait(group, milestone);
1635
1636 account->departure_code = kSOSWithdrewMembership;
1637
1638 dispatch_release(group);
1639 return result;
1640 }
1641
1642
1643 //
1644 // MARK: Application
1645 //
1646
1647 static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
1648 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1649 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1650 CFErrorRef peer_error = NULL;
1651 if (account->trusted_circle && me &&
1652 SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
1653 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
1654 __block bool modified = false;
1655 CFArrayForEach(peer_infos, ^(const void *value) {
1656 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1657 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1658 if (action(circle, account->my_identity, peer)) {
1659 modified = true;
1660 }
1661 }
1662 });
1663 return modified;
1664 });
1665 }
1666 if (peer_error)
1667 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1668 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1669 }
1670
1671 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1672 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1673 if (!user_key)
1674 return false;
1675
1676 __block bool success = true;
1677 __block int64_t num_peers = 0;
1678
1679 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1680 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1681 if (!accepted)
1682 success = false;
1683 else
1684 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1685 return accepted;
1686 });
1687
1688 return success;
1689 }
1690
1691 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1692 __block bool success = true;
1693 __block int64_t num_peers = 0;
1694
1695 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1696 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1697 if (!rejected)
1698 success = false;
1699 else
1700 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1701 return rejected;
1702 });
1703
1704 return success;
1705 }
1706
1707
1708 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1709 return CFSTR("We're compatible, go away");
1710 }
1711
1712 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1713 return account->departure_code;
1714 }
1715
1716 void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
1717 account->departure_code = reason;
1718 }
1719
1720
1721 CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
1722 CFArrayRef result = NULL;
1723 CFNumberRef generation = NULL;
1724
1725 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1726 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1727
1728 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
1729 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1730
1731 fail:
1732 return result;
1733 }
1734
1735 bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
1736 if (!SOSAccountHasPublicKey(account, error))
1737 return NULL;
1738
1739 return account->user_public_trusted;
1740 }
1741
1742 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
1743 // TODO: this result is never set or used
1744 bool result = true;
1745
1746 secnotice("updates", "Ensuring peer registration.");
1747
1748 require_quiet(account->trusted_circle, done);
1749 require_quiet(account->my_identity, done);
1750 // If we are not in the circle, there is no point in setting up peers
1751 require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
1752
1753 // This code only uses the SOSFullPeerInfoRef for two things:
1754 // - Finding out if this device is in the trusted circle
1755 // - Using the peerID for this device to see if the current peer is "me"
1756 // - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
1757
1758 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1759
1760 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
1761 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1762 CFErrorRef localError = NULL;
1763 SOSTransportMessageRef messageTransport = NULL;
1764
1765 if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
1766 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
1767 }
1768 else
1769 messageTransport = account->kvs_message_transport;
1770
1771 SOSPeerCoderInitializeForPeer(messageTransport->engine, account->my_identity, peer, &localError);
1772 if (localError)
1773 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
1774 CFReleaseSafe(localError);
1775 }
1776 });
1777
1778 //Initialize our device ID
1779 SOSTransportMessageIDSGetIDSDeviceID(account);
1780
1781
1782 done:
1783 return result;
1784 }
1785
1786 static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *error) {
1787 if (!account->expansion) {
1788 account->expansion = CFDictionaryCreateMutableForCFTypes(NULL);
1789 }
1790
1791 return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
1792 }
1793
1794 bool SOSAccountClearValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1795 bool success = SOSAccountEnsureExpansion(account, error);
1796 require_quiet(success, errOut);
1797
1798 CFDictionaryRemoveValue(account->expansion, key);
1799 errOut:
1800 return success;
1801 }
1802
1803 bool SOSAccountSetValue(SOSAccountRef account, const void *key, const void *value, CFErrorRef *error) {
1804 bool success = SOSAccountEnsureExpansion(account, error);
1805 require_quiet(success, errOut);
1806
1807 CFDictionarySetValue(account->expansion, key, value);
1808 errOut:
1809 return success;
1810 }
1811
1812
1813 const void *SOSAccountGetValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
1814 if (!account->expansion) {
1815 return NULL;
1816 }
1817 return CFDictionaryGetValue(account->expansion, key);
1818 }
1819
1820 bool SOSAccountAddEscrowRecords(SOSAccountRef account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1821 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1822 CFMutableDictionaryRef escrowCopied = NULL;
1823 bool success = false;
1824
1825 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1826 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1827 else
1828 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1829
1830 CFDictionaryAddValue(escrowCopied, dsid, record);
1831 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1832
1833 if(*error == NULL)
1834 success = true;
1835
1836 CFReleaseNull(escrowCopied);
1837
1838 return success;
1839
1840 }
1841
1842 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1843 bool success = false;
1844
1845 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1846 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1847
1848 return success;
1849 }
1850
1851 bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
1852 {
1853 CFMutableDictionaryRef circleToPeerMessages = NULL;
1854 CFStringRef messageString = NULL;
1855 CFMutableDictionaryRef mutableDictionary = NULL;
1856 CFMutableSetRef peers = NULL;
1857 CFMutableDictionaryRef peerList = NULL;
1858 char* message = NULL;
1859 bool result = false;
1860 if(account->ids_message_transport == NULL)
1861 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1862
1863 require_quiet(account->ids_message_transport, fail);
1864 circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1865
1866 //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
1867
1868 asprintf(&message, "%d", kIDSPeerAvailability);
1869 messageString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
1870
1871 mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("checking peers"), NULL);
1872
1873 //make sure there are peers in the circle
1874 peers = SOSCircleCopyPeers(account->trusted_circle, kCFAllocatorDefault);
1875 require_quiet(CFSetGetCount(peers) > 0, fail);
1876 CFReleaseNull(peers);
1877
1878 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1879 SOSCircleRef circle = account->trusted_circle;
1880
1881 //check each peer to make sure they have the right view set enabled
1882 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1883 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
1884 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account))){
1885 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1886 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1887 if(CFEqualSafe(intersectSets, mySubSet)){
1888 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1889 if(deviceID != NULL)
1890 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), mutableDictionary);
1891 CFReleaseNull(deviceID);
1892 }
1893 CFReleaseNull(peerViews);
1894 CFReleaseNull(intersectSets);
1895 }
1896 });
1897
1898 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1899 CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerList);
1900 result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
1901
1902 fail:
1903 CFReleaseNull(mutableDictionary);
1904 CFReleaseNull(messageString);
1905 CFReleaseNull(peerList);
1906 CFReleaseNull(circleToPeerMessages);
1907 CFReleaseNull(peers);
1908 free(message);
1909 return result;
1910 }
1911
1912
1913 static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
1914 if (!SOSAccountIsInCircle(account, NULL))
1915 return;
1916
1917 SOSAccountModifyCircle(account, NULL, ^bool (SOSCircleRef circle) {
1918 __block bool updated = false;
1919 CFSetForEach(account->retirees, ^(CFTypeRef element){
1920 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1921
1922 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1923 updated = true;
1924 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1925 CFErrorRef cleanupError = NULL;
1926 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
1927 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1928 CFReleaseSafe(cleanupError);
1929 }
1930 });
1931 return updated;
1932 });
1933 }
1934
1935 void SOSAccountFinishTransaction(SOSAccountRef account) {
1936 if(account->circle_rings_retirements_need_attention){
1937 SOSAccountRecordRetiredPeersInCircle(account);
1938
1939 CFErrorRef localError = NULL;
1940 if(!SOSTransportCircleFlushChanges(account->circle_transport, &localError)) {
1941 secerror("flush circle failed %@", localError);
1942 }
1943 CFReleaseSafe(localError);
1944
1945 SOSAccountNotifyEngines(account); // For now our only rings are backup rings.
1946 }
1947
1948 SOSAccountCheckHasBeenInSync(account);
1949
1950 account->circle_rings_retirements_need_attention = false;
1951 }
1952