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