]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
c428a071c77c3f93ea713fa05e47deb47651d5c2
[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/SOSPeerInfo.h>
12 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
13 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
14 #include <Security/SecureObjectSync/SOSTransportCircle.h>
15 #include <Security/SecureObjectSync/SOSTransportMessage.h>
16 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
17 #include <Security/SecureObjectSync/SOSKVSKeys.h>
18 #include <Security/SecureObjectSync/SOSTransport.h>
19 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
20 #include <Security/SecureObjectSync/SOSTransportKeyParameterKVS.h>
21 #include <Security/SecureObjectSync/SOSPeerCoder.h>
22 #include <Security/SecureObjectSync/SOSInternal.h>
23 #include <Security/SecureObjectSync/SOSRing.h>
24 #include <Security/SecureObjectSync/SOSRingUtils.h>
25 #include <Security/SecureObjectSync/SOSRingRecovery.h>
26 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
27 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
28 #include <Security/SecureObjectSync/SOSAccountGhost.h>
29
30 #include <Security/SecItemInternal.h>
31 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
32
33 #include <utilities/SecCFWrappers.h>
34 #include <utilities/SecCFError.h>
35
36 CFGiblisWithCompareFor(SOSAccount);
37
38 const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
39 const CFStringRef SOSTransportMessageTypeIDSV2 = CFSTR("IDS2.0");
40 const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
41 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
42 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
43 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
44 const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
45 const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
46 const CFStringRef kSOSTestV2Settings = CFSTR("v2dictionary");
47 const CFStringRef kSOSRecoveryKey = CFSTR("RecoveryKey");
48 const CFStringRef kSOSRecoveryRing = CFSTR("RecoveryRing");
49 const CFStringRef kSOSAccountUUID = CFSTR("UUID");
50
51 #define DATE_LENGTH 25
52 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
53
54 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
55 {
56 bool result = false;
57 CFStringRef circle_name = NULL;
58
59 require_quiet(a, xit);
60 require_quiet(a->factory, xit);
61
62 circle_name = SOSDataSourceFactoryCopyName(a->factory);
63 require(circle_name, xit);
64
65 SOSAccountEnsureCircle(a, circle_name, NULL);
66
67 result = true;
68
69 xit:
70 // We don't own name, so don't release it.
71 CFReleaseNull(circle_name);
72 return result;
73 }
74
75
76 SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
77 CFDictionaryRef gestalt,
78 SOSDataSourceFactoryRef factory) {
79 SOSAccountRef a = CFTypeAllocate(SOSAccount, struct __OpaqueSOSAccount, allocator);
80
81 a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
82
83 a->gestalt = CFRetainSafe(gestalt);
84
85 a->trusted_circle = NULL;
86 a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
87 a->my_identity = NULL;
88 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
89
90 a->factory = factory; // We adopt the factory. kthanksbai.
91
92 a->isListeningForSync = false;
93
94 a->_user_private = NULL;
95 a->_password_tmp = NULL;
96 a->user_private_timer = NULL;
97 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
98
99 a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
100 a->waitForInitialSync_blocks = NULL;
101 a->departure_code = kSOSNeverAppliedToCircle;
102
103 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
104 a->circle_transport = NULL;
105 a->kvs_message_transport = NULL;
106 a->ids_message_transport = NULL;
107 a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
108
109 SOSAccountAddRingDictionary(a);
110
111 a->saveBlock = NULL;
112 a->circle_rings_retirements_need_attention = false;
113 a->engine_peer_state_needs_repair = false;
114 a->key_interests_need_updating = false;
115 a->deviceID = NULL;
116
117 return a;
118 }
119
120 //
121 // MARK: Transactional
122 //
123
124 void SOSAccountWithTransaction_Locked(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
125 SOSAccountTransactionRef at = SOSAccountTransactionCreate(account);
126 action(account, at);
127 SOSAccountTransactionFinish(at);
128 CFReleaseNull(at);
129 }
130
131
132
133 void SOSAccountWithTransaction(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
134 dispatch_block_t with_transaction = ^{
135 SOSAccountWithTransaction_Locked(account, action);
136 };
137
138 if (sync) {
139 dispatch_sync(SOSAccountGetQueue(account), with_transaction);
140 } else {
141 dispatch_async(SOSAccountGetQueue(account), with_transaction);
142 }
143 }
144
145 void SOSAccountWithTransactionSync(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
146 SOSAccountWithTransaction(account, true, action);
147 }
148
149 void SOSAccountWithTransactionAsync(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
150 SOSAccountWithTransaction(account, false, action);
151 }
152
153 //
154 // MARK: Save Block
155 //
156
157 void SOSAccountSetSaveBlock(SOSAccountRef account, SOSAccountSaveBlock saveBlock) {
158 CFAssignRetained(account->saveBlock, Block_copy(saveBlock));
159 }
160
161 void SOSAccountFlattenToSaveBlock(SOSAccountRef account) {
162 if (account->saveBlock) {
163 CFErrorRef localError = NULL;
164 CFDataRef saveData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &localError);
165
166 (account->saveBlock)(saveData, localError);
167
168 CFReleaseNull(saveData);
169 CFReleaseNull(localError);
170 }
171 }
172
173 //
174 // MARK: Security Properties
175 //
176
177 SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
178 SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
179 bool updateCircle = false;
180 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
181 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
182 retval = SOSFullPeerInfoUpdateSecurityProperty(account->my_identity, actionCode, property, error);
183
184 if(actionCode == kSOSCCSecurityPropertyEnable && retval == kSOSCCSecurityPropertyValid) {
185 updateCircle = true;
186 } else if(actionCode == kSOSCCSecurityPropertyDisable && retval == kSOSCCSecurityPropertyNotValid) {
187 updateCircle = true;
188 } else if(actionCode == kSOSCCSecurityPropertyPending) {
189 updateCircle = true;
190 }
191
192 if (updateCircle) {
193 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
194 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for security property change");
195 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
196 });
197 }
198
199 errOut:
200 return retval;
201 }
202
203 SOSSecurityPropertyResultCode SOSAccountSecurityPropertyStatus(SOSAccountRef account, CFStringRef property, CFErrorRef *error) {
204 SOSSecurityPropertyResultCode retval = kSOSCCGeneralViewError;
205 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
206 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
207 retval = SOSFullPeerInfoSecurityPropertyStatus(account->my_identity, property, error);
208 errOut:
209 return retval;
210 }
211
212 bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
213 {
214 if (CFEqualSafe(new_gestalt, account->gestalt))
215 return false;
216
217 if (account->trusted_circle && account->my_identity
218 && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
219 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
220 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
221 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
222 });
223 }
224
225 CFRetainAssign(account->gestalt, new_gestalt);
226 return true;
227 }
228
229 CFDictionaryRef SOSAccountCopyGestalt(SOSAccountRef account) {
230 return CFDictionaryCreateCopy(kCFAllocatorDefault, account->gestalt);
231 }
232
233 bool SOSAccountUpdateV2Dictionary(SOSAccountRef account, CFDictionaryRef newV2Dict) {
234 if(!newV2Dict) return true;
235 SOSAccountSetValue(account, kSOSTestV2Settings, newV2Dict, NULL);
236 if (account->trusted_circle && account->my_identity
237 && SOSFullPeerInfoUpdateV2Dictionary(account->my_identity, newV2Dict, NULL)) {
238 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
239 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
240 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
241 });
242 }
243 return true;
244 }
245
246 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccountRef account) {
247 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
248 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
249 }
250
251 static bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
252 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
253 //send new DSID over account changed
254 SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
255 return true;
256 }
257
258 void SOSAccountAssertDSID(SOSAccountRef account, CFStringRef dsid) {
259 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
260 if(accountDSID == NULL) {
261 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
262
263 SOSAccountUpdateDSID(account, dsid);
264 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
265 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
266
267 //DSID has changed, blast the account!
268 SOSAccountSetToNew(account);
269
270 //update DSID to the new DSID
271 SOSAccountUpdateDSID(account, dsid);
272 } else {
273 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
274 }
275 }
276
277 bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews, CFSetRef excludedViews) {
278 if (account->trusted_circle && account->my_identity) {
279 if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
280 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
281 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
282 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
283 });
284 }
285 }
286
287 return true;
288 }
289
290 void SOSAccountPendEnableViewSet(SOSAccountRef account, CFSetRef enabledViews)
291 {
292 if(CFSetGetValue(enabledViews, kSOSViewKeychainV0) != NULL) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
293
294 SOSAccountValueUnionWith(account, kSOSPendingEnableViewsToBeSetKey, enabledViews);
295 SOSAccountValueSubtractFrom(account, kSOSPendingDisableViewsToBeSetKey, enabledViews);
296 }
297
298
299 void SOSAccountPendDisableViewSet(SOSAccountRef account, CFSetRef disabledViews)
300 {
301 SOSAccountValueUnionWith(account, kSOSPendingDisableViewsToBeSetKey, disabledViews);
302 SOSAccountValueSubtractFrom(account, kSOSPendingEnableViewsToBeSetKey, disabledViews);
303 }
304
305 static SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccountRef account, SOSViewActionCode actionCode) {
306 SOSViewResultCode retval = kSOSCCGeneralViewError;
307 // The V0 view switches on and off all on it's own, we allow people the delusion
308 // of control and status if it's what we're stuck at., otherwise error.
309 if (SOSAccountSyncingV0(account)) {
310 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
311 retval = kSOSCCViewMember;
312 } else {
313 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
314 retval = kSOSCCViewNotMember;
315 }
316 errOut:
317 return retval;
318 }
319
320
321 SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
322 SOSViewResultCode retval = kSOSCCGeneralViewError;
323 SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
324 bool alreadyInSync = SOSAccountHasCompletedInitialSync(account);
325
326 bool updateCircle = false;
327 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
328 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
329 require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
330 currentStatus = SOSAccountViewStatus(account, viewname, error);
331 require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
332
333 if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
334 retval = SOSAccountVirtualV0Behavior(account, actionCode);
335 } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
336 // Subviews of V0 syncing can't be turned off if V0 is on.
337 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
338 retval = kSOSCCViewMember;
339 } else {
340 CFMutableSetRef pendingSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
341 CFSetAddValue(pendingSet, viewname);
342
343 if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
344 if(alreadyInSync) {
345 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
346 if(retval == kSOSCCViewMember) updateCircle = true;
347 } else {
348 SOSAccountPendEnableViewSet(account, pendingSet);
349 retval = kSOSCCViewMember;
350 updateCircle = false;
351 }
352 } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
353 if(alreadyInSync) {
354 retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
355 if(retval == kSOSCCViewNotMember) updateCircle = true;
356 } else {
357 SOSAccountPendDisableViewSet(account, pendingSet);
358 retval = kSOSCCViewNotMember;
359 updateCircle = false;
360 }
361 } else {
362 retval = currentStatus;
363 }
364
365 CFReleaseNull(pendingSet);
366
367 if (updateCircle) {
368 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
369 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
370 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
371 });
372 }
373 }
374
375 errOut:
376 return retval;
377 }
378
379 SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewname, CFErrorRef *error) {
380 SOSViewResultCode retval = kSOSCCGeneralViewError;
381 require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
382 require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
383
384 if (SOSAccountValueSetContainsValue(account, kSOSPendingEnableViewsToBeSetKey, viewname)) {
385 retval = kSOSCCViewMember;
386 } else if (SOSAccountValueSetContainsValue(account, kSOSPendingDisableViewsToBeSetKey, viewname)) {
387 retval = kSOSCCViewNotMember;
388 } else {
389 retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
390 }
391
392 // 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
393 if (retval != kSOSCCViewMember) {
394 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
395 && SOSAccountSyncingV0(account)) {
396 retval = kSOSCCViewMember;
397 }
398 }
399
400 // If we're only an applicant we report pending if we would be a view member
401 if (retval == kSOSCCViewMember) {
402 bool isApplicant = SOSCircleHasApplicant(account->trusted_circle, SOSAccountGetMyPeerInfo(account), error);
403 if (isApplicant) {
404 retval = kSOSCCViewPending;
405 }
406 }
407
408 errOut:
409 return retval;
410 }
411
412 static void dumpViewSet(CFStringRef label, CFSetRef views) {
413 if(views) {
414 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
415 secnotice("circleChange", "%@ list: %@", label, description);
416 });
417 } else {
418 secnotice("circleChange", "No %@ list provided.", label);
419 }
420 }
421
422 static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account, CFMutableSetRef viewSet, SOSViewActionCode actionCode) {
423 bool retval = true;
424 if(viewSet && CFSetContainsValue(viewSet, kSOSViewKeychainV0)) {
425 retval = SOSAccountVirtualV0Behavior(account, actionCode) != kSOSCCGeneralViewError;
426 CFSetRemoveValue(viewSet, kSOSViewKeychainV0);
427 }
428 return retval;
429 }
430
431 bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef origEnabledViews, CFSetRef origDisabledViews) {
432 bool retval = false;
433 bool updateCircle = false;
434 SOSPeerInfoRef pi = NULL;
435 CFMutableSetRef enabledViews = NULL;
436 CFMutableSetRef disabledViews = NULL;
437 if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
438 if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
439 dumpViewSet(CFSTR("Enabled"), enabledViews);
440 dumpViewSet(CFSTR("Disabled"), disabledViews);
441
442 require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
443
444 // Make sure we have a peerInfo capable of supporting views.
445 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
446 require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
447 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
448
449 pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
450
451 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
452
453 if(!SOSPeerInfoVersionIsCurrent(pi)) {
454 CFErrorRef updateFailure = NULL;
455 require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
456 (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
457 secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
458 updateCircle = true;
459 }
460
461 CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
462 secnotice("viewChange", "Enabling %@", description);
463 });
464
465 CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
466 secnotice("viewChange", "Disabling %@", description);
467 });
468
469 require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
470 require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
471
472 if(SOSAccountHasCompletedInitialSync(account)) {
473 if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
474 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
475 retval = true;
476 } else {
477 //hold on to the views and enable them later
478 if(enabledViews) SOSAccountPendEnableViewSet(account, enabledViews);
479 if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
480 retval = true;
481 }
482
483 if(updateCircle) {
484 /* UPDATE FULLPEERINFO VIEWS */
485 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
486
487 require_quiet(SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
488 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
489 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
490 }), errOut);
491
492 // Make sure we update the engine
493 account->circle_rings_retirements_need_attention = true;
494 }
495
496 errOut:
497 CFReleaseNull(enabledViews);
498 CFReleaseNull(disabledViews);
499 CFReleaseNull(pi);
500 return retval;
501 }
502
503
504 SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
505 CFDictionaryRef gestalt,
506 SOSDataSourceFactoryRef factory) {
507 SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
508
509 SOSAccountEnsureFactoryCircles(a);
510
511 SOSAccountEnsureUUID(a);
512
513 a->key_interests_need_updating = true;
514
515 return a;
516 }
517
518 static void SOSAccountDestroy(CFTypeRef aObj) {
519 SOSAccountRef a = (SOSAccountRef) aObj;
520
521 // We don't own the factory, merely have a reference to the singleton
522 // Don't free it.
523 // a->factory
524
525 SOSAccountCancelSyncChecking(a);
526
527 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
528
529 if (engine)
530 SOSEngineSetSyncCompleteListenerQueue(engine, NULL);
531
532 dispatch_sync(a->queue, ^{
533 CFReleaseNull(a->gestalt);
534
535 CFReleaseNull(a->my_identity);
536 CFReleaseNull(a->trusted_circle);
537 CFReleaseNull(a->backups);
538 CFReleaseNull(a->retirees);
539
540 a->user_public_trusted = false;
541 CFReleaseNull(a->user_public);
542 CFReleaseNull(a->user_key_parameters);
543
544 SOSAccountPurgePrivateCredential(a);
545 CFReleaseNull(a->previous_public);
546 CFReleaseNull(a->_user_private);
547 CFReleaseNull(a->_password_tmp);
548
549 a->departure_code = kSOSNeverAppliedToCircle;
550 CFReleaseNull(a->kvs_message_transport);
551 CFReleaseNull(a->ids_message_transport);
552 CFReleaseNull(a->key_transport);
553 CFReleaseNull(a->circle_transport);
554 dispatch_release(a->queue);
555
556 dispatch_release(a->user_private_timer);
557 CFReleaseNull(a->change_blocks);
558 CFReleaseNull(a->waitForInitialSync_blocks);
559 CFReleaseNull(a->expansion);
560
561 CFReleaseNull(a->saveBlock);
562 CFReleaseNull(a->deviceID);
563 });
564 }
565
566 static OSStatus do_delete(CFDictionaryRef query) {
567 OSStatus result;
568
569 result = SecItemDelete(query);
570 if (result) {
571 secerror("SecItemDelete: %d", (int)result);
572 }
573 return result;
574 }
575
576 static int
577 do_keychain_delete_aks_bags()
578 {
579 OSStatus result;
580 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
581 kSecClass, kSecClassGenericPassword,
582 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
583 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
584 kSecAttrService, CFSTR("SecureBackupService"),
585 kSecAttrSynchronizable, kCFBooleanTrue,
586 kSecUseTombstones, kCFBooleanFalse,
587 NULL);
588
589 result = do_delete(item);
590 CFReleaseSafe(item);
591
592 return result;
593 }
594
595 static int
596 do_keychain_delete_identities()
597 {
598 OSStatus result;
599 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
600 kSecClass, kSecClassKey,
601 kSecAttrSynchronizable, kCFBooleanTrue,
602 kSecUseTombstones, kCFBooleanFalse,
603 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
604 NULL);
605
606 result = do_delete(item);
607 CFReleaseSafe(item);
608
609 return result;
610 }
611
612 static int
613 do_keychain_delete_lakitu()
614 {
615 OSStatus result;
616 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
617 kSecClass, kSecClassGenericPassword,
618 kSecAttrSynchronizable, kCFBooleanTrue,
619 kSecUseTombstones, kCFBooleanFalse,
620 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
621 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
622 kSecAttrService, CFSTR("EscrowService"),
623 NULL);
624
625 result = do_delete(item);
626 CFReleaseSafe(item);
627
628 return result;
629 }
630
631 static int
632 do_keychain_delete_sbd()
633 {
634 OSStatus result;
635 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
636 kSecClass, kSecClassGenericPassword,
637 kSecAttrSynchronizable, kCFBooleanTrue,
638 kSecUseTombstones, kCFBooleanFalse,
639 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
640 NULL);
641
642 result = do_delete(item);
643 CFReleaseSafe(item);
644
645 return result;
646 }
647
648 void SOSAccountSetToNew(SOSAccountRef a) {
649 secnotice("accountChange", "Setting Account to New");
650 int result = 0;
651
652 CFReleaseNull(a->my_identity);
653 CFReleaseNull(a->trusted_circle);
654 CFReleaseNull(a->backups);
655 CFReleaseNull(a->retirees);
656
657 CFReleaseNull(a->user_key_parameters);
658 CFReleaseNull(a->user_public);
659 CFReleaseNull(a->previous_public);
660 CFReleaseNull(a->_user_private);
661 CFReleaseNull(a->_password_tmp);
662
663 CFReleaseNull(a->key_transport);
664 CFReleaseNull(a->circle_transport);
665 CFReleaseNull(a->kvs_message_transport);
666 CFReleaseNull(a->ids_message_transport);
667 CFReleaseNull(a->expansion);
668 CFReleaseNull(a->deviceID);
669
670 /* remove all syncable items */
671 result = do_keychain_delete_aks_bags(); (void) result;
672 secdebug("set to new", "result for deleting aks bags: %d", result);
673
674 result = do_keychain_delete_identities(); (void) result;
675 secdebug("set to new", "result for deleting identities: %d", result);
676
677 result = do_keychain_delete_lakitu(); (void) result;
678 secdebug("set to new", "result for deleting lakitu: %d", result);
679
680 result = do_keychain_delete_sbd(); (void) result;
681 secdebug("set to new", "result for deleting sbd: %d", result);
682
683 a->user_public_trusted = false;
684 a->departure_code = kSOSNeverAppliedToCircle;
685
686 if (a->user_private_timer) {
687 dispatch_source_cancel(a->user_private_timer);
688 dispatch_release(a->user_private_timer);
689 a->user_private_timer = NULL;
690 xpc_transaction_end();
691
692 }
693 if (a->lock_notification_token != NOTIFY_TOKEN_INVALID) {
694 notify_cancel(a->lock_notification_token);
695 a->lock_notification_token = NOTIFY_TOKEN_INVALID;
696 }
697
698 // keeping gestalt;
699 // keeping factory;
700 // Live Notification
701 // change_blocks;
702 // update_interest_block;
703 // update_block;
704
705 a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
706 a->circle_transport = NULL;
707 a->kvs_message_transport = NULL;
708 a->ids_message_transport = NULL;
709
710 a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
711
712 a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
713 a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
714 SOSAccountAddRingDictionary(a);
715
716 SOSAccountEnsureFactoryCircles(a); // Does rings too
717
718 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
719 SOSAccountEnsureUUID(a);
720
721 a->key_interests_need_updating = true;
722 }
723
724 bool SOSAccountIsNew(SOSAccountRef account, CFErrorRef *error){
725 bool result = false;
726 require_quiet(account->user_public_trusted == false, exit);
727 require_quiet(account->departure_code == kSOSNeverAppliedToCircle, exit);
728 require_quiet(account->user_private_timer == NULL, exit);
729 require_quiet(account->lock_notification_token == NOTIFY_TOKEN_INVALID, exit);
730 require_quiet (CFDictionaryGetCount(account->backups) == 0, exit);
731 require_quiet(CFSetGetCount(account->retirees) == 0, exit);
732
733 result = true;
734 exit:
735 return result;
736 }
737
738 static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
739 SOSAccountRef a = (SOSAccountRef) aObj;
740
741 CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
742
743 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: %c%c%c%c%c G: %@ Me: %@ C: %@ >"), a,
744 a->user_public ? 'P' : 'p',
745 a->user_public_trusted ? 'T' : 't',
746 a->isListeningForSync ? 'L' : 'l',
747 SOSAccountHasCompletedInitialSync(a) ? 'C' : 'c',
748 SOSAccountHasCompletedRequiredBackupSync(a) ? 'B' : 'b',
749 gestaltDescription, a->my_identity, a->trusted_circle);
750
751 CFReleaseNull(gestaltDescription);
752
753 return result;
754 }
755
756 CFStringRef SOSAccountCreateCompactDescription(SOSAccountRef a) {
757
758 CFStringRef gestaltDescription = CFDictionaryCopySuperCompactDescription(a->gestalt);
759
760 CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), gestaltDescription);
761
762 CFReleaseNull(gestaltDescription);
763
764 return result;
765 }
766
767 static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
768 {
769 SOSAccountRef laccount = (SOSAccountRef) lhs;
770 SOSAccountRef raccount = (SOSAccountRef) rhs;
771
772 return CFEqualSafe(laccount->gestalt, raccount->gestalt)
773 && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
774 && CFEqualSafe(laccount->expansion, raccount->expansion)
775 && CFEqualSafe(laccount->my_identity, raccount->my_identity);
776 }
777
778 dispatch_queue_t SOSAccountGetQueue(SOSAccountRef account) {
779 return account->queue;
780 }
781
782 void SOSAccountSetUserPublicTrustedForTesting(SOSAccountRef account){
783 account->user_public_trusted = true;
784 }
785
786 SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef* error)
787 {
788 return CFRetainSafe(account->my_identity);
789 }
790
791
792
793 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
794 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
795 {
796 bool success = true;
797
798 SOSPeerInfoRef myPeerInfo = SOSFullPeerInfoGetPeerInfo(account->my_identity);
799 require_action_quiet(account->my_identity && myPeerInfo, xit, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("I have no peer")));
800 require_quiet(SOSCircleHasActivePeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), error), xit);
801
802 CFStringRef cleanupPeerID = SOSPeerInfoGetPeerID(cleanupPeer);
803
804 CFStringRef circle_name = SOSCircleGetName(circle);
805
806 CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
807 CFArrayAppendValue(CFDictionaryEnsureCFArrayAndGetCurrentValue(circleToPeerIDs, circle_name), cleanupPeerID);
808
809 CFErrorRef localError = NULL;
810 if (!(success &= SOSTransportMessageCleanupAfterPeerMessages(account->kvs_message_transport, circleToPeerIDs, &localError))) {
811 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
812 }
813
814 if (account->ids_message_transport && !SOSTransportMessageCleanupAfterPeerMessages(account->ids_message_transport, circleToPeerIDs, &localError)) {
815 secnotice("account", "Failed to cleanup after peer %@ messages: %@", cleanupPeerID, localError);
816 }
817
818 CFReleaseNull(localError);
819
820 if((success &= SOSPeerInfoRetireRetirementTicket(seconds, cleanupPeer))) {
821 if (!(success &= SOSTransportCircleExpireRetirementRecords(account->circle_transport, circleToPeerIDs, &localError))) {
822 secnotice("account", "Failed to cleanup after peer %@ retirement: %@", cleanupPeerID, localError);
823 }
824 }
825 CFReleaseNull(localError);
826 CFReleaseNull(circleToPeerIDs);
827
828 xit:
829 return success;
830 }
831
832 bool SOSAccountCleanupRetirementTickets(SOSAccountRef account, size_t seconds, CFErrorRef* error) {
833 CFMutableSetRef retirees_to_remove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
834
835 __block bool success = true;
836
837 CFSetForEach(account->retirees, ^(const void *value) {
838 SOSPeerInfoRef retiree = (SOSPeerInfoRef) value;
839
840 if (retiree) {
841 // 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.
842 if (!SOSPeerInfoIsRetirementTicket(retiree) ||
843 (SOSPeerInfoRetireRetirementTicket(seconds, retiree) && !SOSCircleHasActivePeer(account->trusted_circle, retiree, NULL))) {
844 CFSetAddValue(retirees_to_remove, retiree);
845 };
846 }
847 });
848
849 CFMutableArrayRef retirees_to_cleanup = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
850 CFSetForEach(retirees_to_remove, ^(const void *value) {
851 CFArrayAppendValue(retirees_to_cleanup, value);
852 CFSetRemoveValue(account->retirees, value);
853 });
854
855 CFReleaseNull(retirees_to_remove);
856
857 CFDictionaryRef retirements_to_remove = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
858 SOSCircleGetName(account->trusted_circle), retirees_to_cleanup,
859 NULL);
860
861 CFReleaseNull(retirees_to_cleanup);
862
863 success = SOSTransportCircleExpireRetirementRecords(account->circle_transport, retirements_to_remove, error);
864
865 CFReleaseNull(retirements_to_remove);
866
867 return success;
868 }
869
870 bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErrorRef *error) {
871 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
872 CFSetSetValue(account->retirees, peer);
873 CFErrorRef cleanupError = NULL;
874 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, peer, &cleanupError)) {
875 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
876 }
877 CFReleaseSafe(cleanupError);
878 });
879 return true;
880 }
881
882 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
883 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
884 SOSFullPeerInfoRef meFull = SOSAccountGetMyFullPeerInfo(account);
885 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(meFull);
886 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
887
888 if(!new_circle) return NULL;
889 __block bool workDone = false;
890 if (account->retirees) {
891 CFSetForEach(account->retirees, ^(const void* value) {
892 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
893 if (isSOSPeerInfo(pi)) {
894 SOSCircleUpdatePeerInfo(new_circle, pi);
895 workDone = true;
896 }
897 });
898 }
899
900 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
901 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
902
903 if(iAmApplicant) {
904 if(userPrivKey) {
905 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
906 if(!SOSCircleResetToOffering(new_circle, userPrivKey, meFull, error) ||
907 !SOSAccountAddiCloudIdentity(account, new_circle, userPrivKey, error)) {
908 CFReleaseNull(new_circle);
909 return NULL;
910 }
911 } else {
912 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
913 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
914 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
915 // handleUpdateCircle to return false.
916 CFReleaseNull(new_circle);
917 return NULL;
918 }
919 } else {
920 // This case is when we aren't an applicant and the circle is retirement-empty.
921 secnotice("resetToEmpty", "Reset to empty with last retirement");
922 SOSCircleResetToEmpty(new_circle, NULL);
923 }
924 }
925
926 return new_circle;
927 }
928
929 //
930 // MARK: Circle Membership change notificaion
931 //
932
933 void SOSAccountAddChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
934 SOSAccountCircleMembershipChangeBlock copy = Block_copy(changeBlock);
935 CFArrayAppendValue(a->change_blocks, copy);
936 CFReleaseNull(copy);
937 }
938
939 void SOSAccountRemoveChangeBlock(SOSAccountRef a, SOSAccountCircleMembershipChangeBlock changeBlock) {
940 CFArrayRemoveAllValue(a->change_blocks, changeBlock);
941 }
942
943 void SOSAccountAddSyncablePeerBlock(SOSAccountRef a, CFStringRef ds_name, SOSAccountSyncablePeersBlock changeBlock) {
944 if (!changeBlock) return;
945
946 CFRetainSafe(ds_name);
947 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSCircleRef new_circle,
948 CFSetRef added_peers, CFSetRef removed_peers,
949 CFSetRef added_applicants, CFSetRef removed_applicants) {
950
951 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
952 return;
953
954 SOSPeerInfoRef myPi = SOSFullPeerInfoGetPeerInfo(a->my_identity);
955 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
956
957 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
958 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
959 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
960
961 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
962 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
963 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
964 });
965
966 CFSetForEach(added_peers, ^(const void *value) {
967 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
968 });
969
970 CFSetForEach(removed_peers, ^(const void *value) {
971 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
972 });
973 }
974
975 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
976 changeBlock(peer_ids, added_ids, removed_ids);
977
978 CFReleaseSafe(peer_ids);
979 CFReleaseSafe(added_ids);
980 CFReleaseSafe(removed_ids);
981 };
982
983 CFRetainSafe(changeBlock);
984 SOSAccountAddChangeBlock(a, block_to_register);
985
986 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
987 if (a->trusted_circle && CFEqualSafe(ds_name, SOSCircleGetName(a->trusted_circle))) {
988 block_to_register(a->trusted_circle, empty, empty, empty, empty);
989 }
990 CFReleaseSafe(empty);
991 }
992
993 void SOSAccountPurgeIdentity(SOSAccountRef account) {
994 if (account->my_identity) {
995 // Purge private key but don't return error if we can't.
996 CFErrorRef purgeError = NULL;
997 if (!SOSFullPeerInfoPurgePersistentKey(account->my_identity, &purgeError)) {
998 secwarning("Couldn't purge persistent key for %@ [%@]", account->my_identity, purgeError);
999 }
1000 CFReleaseNull(purgeError);
1001
1002 CFReleaseNull(account->my_identity);
1003 }
1004 }
1005
1006 bool sosAccountLeaveCircle(SOSAccountRef account, SOSCircleRef circle, CFErrorRef* error) {
1007 SOSFullPeerInfoRef fpi = account->my_identity;
1008 if(!fpi) return false;
1009
1010 CFErrorRef localError = NULL;
1011
1012 bool retval = false;
1013
1014 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1015 if (!retire_peer) {
1016 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1017 } else {
1018 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1019 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1020 // Remove our application if we have one.
1021 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1022 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1023 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1024 CFErrorRef cleanupError = NULL;
1025 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retire_peer, &cleanupError)) {
1026 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1027 }
1028 CFReleaseSafe(cleanupError);
1029 }
1030 }
1031
1032 // Store the retirement record locally.
1033 CFSetAddValue(account->retirees, retire_peer);
1034
1035 // Write retirement to Transport
1036 CFErrorRef postError = NULL;
1037 if (!SOSTransportCirclePostRetirement(account->circle_transport, SOSCircleGetName(circle), retire_peer, &postError)){
1038 secwarning("Couldn't post retirement (%@)", postError);
1039 }
1040 if(!SOSTransportCircleFlushChanges(account->circle_transport, &postError)){
1041 secwarning("Couldn't flush retirement data (%@)", postError);
1042 }
1043 CFReleaseNull(postError);
1044 }
1045
1046 SOSAccountPurgeIdentity(account);
1047
1048 retval = true;
1049
1050 CFReleaseNull(localError);
1051 CFReleaseNull(retire_peer);
1052 return retval;
1053 }
1054
1055 bool sosAccountLeaveRing(SOSAccountRef account, SOSRingRef ring, CFErrorRef* error) {
1056 SOSFullPeerInfoRef fpi = account->my_identity;
1057 if(!fpi) return false;
1058 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1059 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1060
1061 CFErrorRef localError = NULL;
1062
1063 bool retval = false;
1064 bool writeRing = false;
1065 bool writePeerInfo = false;
1066
1067 if(SOSRingHasPeerID(ring, peerID)) {
1068 writePeerInfo = true;
1069 }
1070
1071 #if 0
1072 // this was circle behavior - at some point
1073 if(SOSRingHasApplicant(ring, peerID)) {
1074 writeRing = true;
1075 }
1076 #endif
1077
1078 if(writePeerInfo || writeRing) {
1079 SOSRingWithdraw(ring, NULL, fpi, error);
1080 }
1081
1082 // Write leave thing to Transport
1083 CFDataRef peerInfoData = SOSFullPeerInfoCopyEncodedData(fpi, kCFAllocatorDefault, error);
1084 SOSTransportCircleSendPeerInfo(account->circle_transport, peerID, peerInfoData, NULL); // TODO: Handle errors?
1085
1086 if (writeRing) {
1087 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1088
1089 if (ring_data) {
1090 SOSTransportCircleRingPostRing(account->circle_transport, SOSRingGetName(ring), ring_data, NULL); // TODO: Handle errors?
1091 }
1092 CFReleaseNull(ring_data);
1093 }
1094 retval = true;
1095 CFReleaseNull(localError);
1096 return retval;
1097 }
1098
1099 bool SOSAccountPostDebugScope(SOSAccountRef account, CFTypeRef scope, CFErrorRef *error) {
1100 bool result = false;
1101 SOSTransportCircleRef transport = account->circle_transport;
1102 if (transport) {
1103 result = SOSTransportCircleSendDebugInfo(transport, kSOSAccountDebugScope, scope, error);
1104 }
1105 return result;
1106 }
1107
1108 /*
1109 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1110 local value that has been overwritten by a distant value. If there is no
1111 conflict between the local and the distant values when doing the initial
1112 sync (e.g. if the cloud has no data stored or the client has not stored
1113 any data yet), you'll never see that notification.
1114
1115 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1116 with server but initial round trip with server does not imply
1117 NSUbiquitousKeyValueStoreInitialSyncChange.
1118 */
1119
1120
1121 //
1122 // MARK: Status summary
1123 //
1124
1125 static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInfoRef this_peer) {
1126 if (!circle)
1127 return kSOSCCNotInCircle;
1128
1129 if (circle && SOSCircleCountPeers(circle) == 0)
1130 return kSOSCCCircleAbsent;
1131
1132 if (this_peer) {
1133
1134 if(SOSPeerInfoIsRetirementTicket(this_peer))
1135 return kSOSCCNotInCircle;
1136
1137 if (SOSCircleHasPeer(circle, this_peer, NULL))
1138 return kSOSCCInCircle;
1139
1140 if (SOSCircleHasApplicant(circle, this_peer, NULL))
1141 return kSOSCCRequestPending;
1142 }
1143
1144 return kSOSCCNotInCircle;
1145 }
1146
1147 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1148 switch(status) {
1149 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1150 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1151 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1152 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1153 case kSOSCCError: return CFSTR("kSOSCCError");
1154 }
1155 return CFSTR("kSOSCCError");
1156 }
1157
1158 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
1159 SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
1160
1161 if (result != kSOSCCInCircle) {
1162 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
1163 return false;
1164 }
1165
1166 return true;
1167 }
1168
1169 SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error) {
1170 if (!SOSAccountHasPublicKey(account, error)) {
1171 return kSOSCCError;
1172 }
1173
1174 return SOSCCThisDeviceStatusInCircle(account->trusted_circle, SOSAccountGetMyPeerInfo(account));
1175 }
1176
1177 //
1178 // MARK: Account Reset Circles
1179 //
1180
1181 // This needs to be called within a SOSAccountModifyCircle() block
1182
1183 bool SOSAccountAddiCloudIdentity(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
1184 bool result = false;
1185 SOSFullPeerInfoRef cloud_identity = NULL;
1186 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
1187 require_quiet(cloud_peer, err_out);
1188 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
1189 CFReleaseNull(cloud_peer);
1190 require_quiet(cloud_identity, err_out);
1191 require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, error), err_out);
1192 require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), error), err_out);
1193 result = true;
1194 err_out:
1195 return result;
1196 }
1197
1198 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1199 bool retval = false;
1200 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1201
1202 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1203 if(SOSPeerInfoIsCloudIdentity(peer)) {
1204 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1205 if(!icfpi) {
1206 CFSetAddValue(iCloud2Remove, peer);
1207 }
1208 CFReleaseNull(icfpi);
1209 }
1210 });
1211
1212 if(CFSetGetCount(iCloud2Remove) > 0) {
1213 retval = true;
1214 SOSCircleRemovePeers(circle, privKey, account->my_identity, iCloud2Remove, error);
1215 }
1216 CFReleaseNull(iCloud2Remove);
1217 return retval;
1218 }
1219
1220 static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn, SecKeyRef user_key, CFErrorRef *error) {
1221 SOSAccountRef account = aTxn->account;
1222 bool result = false;
1223
1224 require(SOSAccountHasCircle(account, error), fail);
1225 require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1226
1227 (void) SOSAccountResetAllRings(account, error);
1228
1229 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1230 bool result = false;
1231 SOSFullPeerInfoRef cloud_identity = NULL;
1232 CFErrorRef localError = NULL;
1233
1234 require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
1235
1236 account->departure_code = kSOSNeverLeftCircle;
1237 require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
1238
1239 require_quiet(SOSAccountAddiCloudIdentity(account, circle, user_key, error), err_out);
1240 result = true;
1241 SOSAccountPublishCloudParameters(account, NULL);
1242
1243 err_out:
1244 if (result == false)
1245 secerror("error resetting circle (%@) to offering: %@", circle, localError);
1246 if (localError && error && *error == NULL) {
1247 *error = localError;
1248 localError = NULL;
1249 }
1250 CFReleaseNull(localError);
1251 CFReleaseNull(cloud_identity);
1252 return result;
1253 });
1254
1255 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1256 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1257
1258 result = true;
1259
1260 fail:
1261 return result;
1262 }
1263
1264
1265 bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1266 SOSAccountRef account = aTxn->account;
1267 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1268 if (!user_key)
1269 return false;
1270
1271 CFReleaseNull(account->my_identity);
1272 secnotice("resetToOffering", "Resetting circle to offering by request from client");
1273
1274 return user_key && SOSAccountResetCircleToOffering(aTxn, user_key, error);
1275 }
1276
1277 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
1278 if (!SOSAccountHasPublicKey(account, error))
1279 return false;
1280 __block bool result = true;
1281
1282 result &= SOSAccountResetAllRings(account, error);
1283
1284 CFReleaseNull(account->my_identity);
1285
1286 account->departure_code = kSOSWithdrewMembership;
1287 secnotice("resetToEmpty", "Reset Circle to empty by client request");
1288 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1289 result = SOSCircleResetToEmpty(circle, error);
1290 return result;
1291 });
1292
1293 if (!result) {
1294 secerror("error: %@", error ? *error : NULL);
1295 }
1296 return result;
1297 }
1298 //
1299 // MARK: start backups
1300 //
1301
1302 bool SOSAccountEnsureInBackupRings(SOSAccountRef account) {
1303 __block bool result = false;
1304 __block CFErrorRef error = NULL;
1305 secnotice("backup", "Ensuring in rings");
1306
1307 CFDataRef backupKey = NULL;
1308
1309 require_action_quiet(account->backup_key, exit, result = true);
1310
1311 backupKey = SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey);
1312
1313 bool updateBackupKey = !CFEqualSafe(backupKey, account->backup_key);
1314
1315 if(updateBackupKey) {
1316 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1317 return SOSFullPeerInfoUpdateBackupKey(fpi, account->backup_key, error);
1318 }), exit);
1319 }
1320 require_quiet(account->backup_key, exit); // If it went null, we're done now.
1321
1322 require_quiet(SOSBSKBIsGoodBackupPublic(account->backup_key, &error), exit);
1323
1324 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1325
1326 if(updateBackupKey || recoveryKeyBackFromRing) {
1327 // It's a good key, we're going with it. Stop backing up the old way.
1328 CFErrorRef localError = NULL;
1329 if (!SOSDeleteV0Keybag(&localError)) {
1330 secerror("Failed to delete v0 keybag: %@", localError);
1331 }
1332 CFReleaseNull(localError);
1333
1334 result = true;
1335
1336 // Setup backups the new way.
1337 SOSAccountForEachBackupView(account, ^(const void *value) {
1338 CFStringRef viewName = (CFStringRef)value;
1339 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1340 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1341 }
1342 });
1343 }
1344
1345 exit:
1346 if (!result) {
1347 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
1348 }
1349 CFReleaseNull(backupKey);
1350 return result;
1351 }
1352
1353 //
1354 // MARK: Recovery Public Key Functions
1355 //
1356
1357 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransactionRef txn, CFDataRef recovery_key, CFErrorRef *error){
1358 bool retval = SOSAccountSetRecoveryKey(txn->account, recovery_key, error);
1359 if(retval) secnotice("recovery", "successfully registered recovery public key");
1360 else secnotice("recovery", "could not register recovery public key: %@", *error);
1361 SOSClearErrorIfTrue(retval, error);
1362 return retval;
1363 }
1364
1365 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransactionRef txn, CFDataRef recovery_key, CFErrorRef *error){
1366 bool retval = SOSAccountRemoveRecoveryKey(txn->account, error);
1367 SOSClearErrorIfTrue(retval, error);
1368 return retval;
1369 }
1370
1371 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransactionRef txn, CFErrorRef *error){
1372 CFDataRef result = NULL;
1373 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn->account, error);
1374 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1375
1376 if (!isData(result)) {
1377 CFReleaseNull(result);
1378 }
1379 SOSClearErrorIfTrue(result != NULL, error);
1380
1381 return result;
1382 }
1383
1384 //
1385 // MARK: Joining
1386 //
1387
1388 static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn, SecKeyRef user_key,
1389 bool use_cloud_peer, CFErrorRef* error) {
1390 SOSAccountRef account = aTxn->account;
1391
1392 __block bool result = false;
1393 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1394
1395 require_action_quiet(account->trusted_circle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1396 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), fail);
1397
1398 SOSFullPeerInfoRef myCirclePeer = account->my_identity;
1399
1400 if (SOSCircleCountPeers(account->trusted_circle) == 0 || SOSAccountGhostResultsInReset(account)) {
1401 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1402 // this also clears initial sync data
1403 result = SOSAccountResetCircleToOffering(aTxn, user_key, error);
1404 } else {
1405 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1406
1407 if (use_cloud_peer) {
1408 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
1409 }
1410
1411 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1412 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1413 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1414 account->departure_code = kSOSNeverLeftCircle;
1415 if(result && cloud_full_peer) {
1416 CFErrorRef localError = NULL;
1417 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1418 require_quiet(cloudid, finish);
1419 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1420 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1421
1422 finish:
1423 if (localError){
1424 secerror("Failed to join with cloud identity: %@", localError);
1425 CFReleaseNull(localError);
1426 }
1427 }
1428 return result;
1429 });
1430
1431 if (use_cloud_peer) {
1432 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1433 }
1434 }
1435
1436 fail:
1437 CFReleaseNull(cloud_full_peer);
1438 return result;
1439 }
1440
1441 static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn, bool use_cloud_identity, CFErrorRef* error) {
1442 SOSAccountRef account = aTxn->account;
1443 bool success = false;
1444
1445 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1446 require_quiet(user_key, done); // Fail if we don't get one.
1447
1448 require_action_quiet(account->trusted_circle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1449
1450 if (account->my_identity != NULL) {
1451 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1452 success = SOSCircleHasPeer(account->trusted_circle, myPeer, NULL);
1453 require_quiet(!success, done);
1454
1455 SOSCircleRemoveRejectedPeer(account->trusted_circle, myPeer, NULL); // If we were rejected we should remove it now.
1456
1457 if (!SOSCircleHasApplicant(account->trusted_circle, myPeer, NULL)) {
1458 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(account->trusted_circle));
1459
1460 CFReleaseNull(account->my_identity);
1461 myPeer = NULL;
1462 }
1463 }
1464
1465 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1466
1467 require_quiet(success, done);
1468
1469 account->departure_code = kSOSNeverLeftCircle;
1470
1471 done:
1472 return success;
1473 }
1474
1475 bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1476 secnotice("circleJoin", "Normal path circle join (SOSAccountJoinCircles)");
1477 return SOSAccountJoinCircles_internal(aTxn, false, error);
1478 }
1479
1480 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
1481 CFStringRef result = NULL;
1482
1483 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1484
1485 result = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1486
1487 fail:
1488 return result;
1489 }
1490
1491 bool SOSAccountSetMyDSID(SOSAccountTransactionRef txn, CFStringRef IDS, CFErrorRef* error){
1492 bool result = true;
1493 SOSAccountRef account = txn->account;
1494
1495 secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
1496 if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
1497 require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
1498
1499 result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
1500
1501 SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
1502 SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDSV2, error);
1503 SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanFalse, error);
1504 SOSFullPeerInfoUpdateTransportFragmentationPreference(account->my_identity, kCFBooleanTrue, error);
1505 SOSFullPeerInfoUpdateTransportAckModelPreference(account->my_identity, kCFBooleanTrue, error);
1506 return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
1507 });
1508 }
1509 else
1510 result = false;
1511
1512 // Initiate sync with all IDS peers, since we just learned we can talk that way.
1513 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1514 if (SOSPeerInfoShouldUseIDSTransport(SOSAccountGetMyPeerInfo(account), peer)) {
1515 SOSAccountTransactionAddSyncRequestForPeerID(txn, SOSPeerInfoGetPeerID(peer));
1516 }
1517 });
1518
1519 fail:
1520 CFReleaseNull(account->deviceID);
1521 account->deviceID = CFRetainSafe(IDS);
1522 return result;
1523 }
1524
1525 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1526 bool result = true;
1527 //construct message dictionary, circle -> peerID -> message
1528
1529 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1530
1531 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSSendOneMessage);
1532 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1533 kIDSOperationType, operationString,
1534 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1535 NULL);
1536
1537 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1538 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1539 });
1540
1541 result = SOSTransportMessageSendMessages(account->ids_message_transport, peerToMessage, error);
1542
1543 CFReleaseNull(peerToMessage);
1544 CFReleaseNull(operationString);
1545 CFReleaseNull(rawMessage);
1546 return result;
1547 }
1548
1549 bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
1550 bool result = false;
1551 //construct message dictionary, circle -> peerID -> message
1552
1553 if(account->ids_message_transport == NULL)
1554 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1555
1556 require_quiet(account->ids_message_transport, fail);
1557 CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1558
1559 CFStringRef operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSStartPingTestMessage);
1560 CFDictionaryRef rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1561 kIDSOperationType, operationString,
1562 kIDSMessageToSendKey, CFSTR("send IDS test message"),
1563 NULL);
1564
1565
1566 SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
1567 CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), rawMessage);
1568 });
1569
1570 result = SOSTransportMessageSendMessages(account->ids_message_transport, peerToMessage, error);
1571
1572 CFReleaseNull(peerToMessage);
1573 CFReleaseNull(rawMessage);
1574 CFReleaseNull(operationString);
1575 fail:
1576 return result;
1577 }
1578
1579 bool SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(SOSAccountRef account, CFErrorRef *error){
1580 bool result = true;
1581
1582 __block bool success = true;
1583 __block CFErrorRef localError = NULL;
1584 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
1585 dispatch_retain(wait_for); // Both this scope and the block own it
1586
1587 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
1588 success = (sync_error == NULL);
1589 if (!success) {
1590 CFRetainAssign(localError, sync_error);
1591 }
1592
1593 dispatch_semaphore_signal(wait_for);
1594 dispatch_release(wait_for);
1595 });
1596
1597 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
1598 dispatch_release(wait_for);
1599
1600 if(!success && localError != NULL && error != NULL){
1601 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", localError);
1602 *error = localError;
1603 result = false;
1604 }
1605 else{
1606 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
1607 }
1608 return result;
1609 }
1610
1611 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
1612 secnotice("circleJoin", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1613 return SOSAccountJoinCircles_internal(aTxn, true, error);
1614 }
1615
1616
1617 bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
1618 {
1619 bool result = true;
1620
1621 secnotice("leaveCircle", "Leaving circle by client request");
1622 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1623 return sosAccountLeaveCircle(account, circle, error);
1624 });
1625
1626 account->departure_code = kSOSWithdrewMembership;
1627
1628 return result;
1629 }
1630
1631 bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CFErrorRef* error)
1632 {
1633 bool result = false;
1634 CFMutableSetRef peersToRemove = NULL;
1635 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1636 require_action_quiet(user_key, errOut, secnotice("removePeers", "Can't remove without userKey"));
1637
1638 SOSFullPeerInfoRef me_full = SOSAccountGetMyFullPeerInfo(account);
1639 SOSPeerInfoRef me = SOSAccountGetMyPeerInfo(account);
1640 require_action_quiet(me_full && me, errOut, {
1641 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1642 secnotice("removePeers", "Can't remove without being active peer");
1643 });
1644
1645 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1646
1647 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1648 require_action_quiet(peersToRemove, errOut, secnotice("removePeers", "No peerSet to remove"));
1649
1650 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1651 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1652 CFSetRemoveValue(peersToRemove, me);
1653
1654 result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1655 bool success = false;
1656
1657 if(CFSetGetCount(peersToRemove) != 0) {
1658 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1659 success = SOSAccountGenerationSignatureUpdate(account, error);
1660 } else success = true;
1661
1662 if (success && leaveCircle) {
1663 secnotice("leaveCircle", "Leaving circle by client request");
1664 success = sosAccountLeaveCircle(account, circle, error);
1665 }
1666
1667 done:
1668 return success;
1669
1670 });
1671
1672 errOut:
1673 CFReleaseNull(peersToRemove);
1674 return result;
1675 }
1676
1677
1678 bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef* error) {
1679 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1680 dispatch_group_t group = dispatch_group_create();
1681 __block bool result = false;
1682 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1683 // Add a task to the group
1684 dispatch_group_async(group, queue, ^{
1685 SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
1686 secnotice("leaveCircle", "Leaving circle by client request");
1687 return sosAccountLeaveCircle(account, circle, error);
1688 });
1689 });
1690 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1691 dispatch_group_wait(group, milestone);
1692
1693 account->departure_code = kSOSWithdrewMembership;
1694
1695 dispatch_release(group);
1696 return result;
1697 }
1698
1699
1700 //
1701 // MARK: Application
1702 //
1703
1704 static void for_each_applicant_in_each_circle(SOSAccountRef account, CFArrayRef peer_infos,
1705 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1706 SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(account->my_identity);
1707 CFErrorRef peer_error = NULL;
1708 if (account->trusted_circle && me &&
1709 SOSCircleHasPeer(account->trusted_circle, me, &peer_error)) {
1710 SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle) {
1711 __block bool modified = false;
1712 CFArrayForEach(peer_infos, ^(const void *value) {
1713 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1714 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1715 if (action(circle, account->my_identity, peer)) {
1716 modified = true;
1717 }
1718 }
1719 });
1720 return modified;
1721 });
1722 }
1723 if (peer_error)
1724 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1725 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1726 }
1727
1728 bool SOSAccountAcceptApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1729 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1730 if (!user_key)
1731 return false;
1732
1733 __block bool success = true;
1734 __block int64_t num_peers = 0;
1735
1736 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1737 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1738 if (!accepted)
1739 success = false;
1740 else
1741 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1742 return accepted;
1743 });
1744
1745 return success;
1746 }
1747
1748 bool SOSAccountRejectApplicants(SOSAccountRef account, CFArrayRef applicants, CFErrorRef* error) {
1749 __block bool success = true;
1750 __block int64_t num_peers = 0;
1751
1752 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1753 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1754 if (!rejected)
1755 success = false;
1756 else
1757 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1758 return rejected;
1759 });
1760
1761 return success;
1762 }
1763
1764
1765 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccountRef account, CFErrorRef* error) {
1766 return CFSTR("We're compatible, go away");
1767 }
1768
1769 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccountRef account, CFErrorRef* error) {
1770 return account->departure_code;
1771 }
1772
1773 void SOSAccountSetLastDepartureReason(SOSAccountRef account, enum DepartureReason reason) {
1774 account->departure_code = reason;
1775 }
1776
1777
1778 CFArrayRef SOSAccountCopyGeneration(SOSAccountRef account, CFErrorRef *error) {
1779 CFArrayRef result = NULL;
1780 CFNumberRef generation = NULL;
1781
1782 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1783 require_action_quiet(account->trusted_circle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1784
1785 generation = (CFNumberRef)SOSCircleGetGeneration(account->trusted_circle);
1786 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1787
1788 fail:
1789 return result;
1790 }
1791
1792 bool SOSValidateUserPublic(SOSAccountRef account, CFErrorRef *error) {
1793 if (!SOSAccountHasPublicKey(account, error))
1794 return NULL;
1795
1796 return account->user_public_trusted;
1797 }
1798
1799 bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error) {
1800 // TODO: this result is never set or used
1801 bool result = true;
1802
1803 secnotice("updates", "Ensuring peer registration.");
1804
1805 require_quiet(account->trusted_circle, done);
1806 require_quiet(account->my_identity, done);
1807 require_quiet(account->user_public_trusted, done);
1808
1809 // If we are not in the circle, there is no point in setting up peers
1810 require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
1811
1812 // This code only uses the SOSFullPeerInfoRef for two things:
1813 // - Finding out if this device is in the trusted circle
1814 // - Using the peerID for this device to see if the current peer is "me"
1815 // - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder
1816
1817 CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
1818
1819 SOSCircleForEachValidSyncingPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
1820 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1821 CFErrorRef localError = NULL;
1822 SOSTransportMessageRef messageTransport = NULL;
1823
1824 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
1825
1826 SOSEngineInitializePeerCoder(messageTransport->engine, account->my_identity, peer, &localError);
1827 if (localError)
1828 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
1829 CFReleaseSafe(localError);
1830 }
1831 });
1832
1833 //Initialize our device ID
1834 SOSTransportMessageIDSGetIDSDeviceID(account);
1835
1836
1837 done:
1838 return result;
1839 }
1840
1841 bool SOSAccountAddEscrowRecords(SOSAccountRef account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1842 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1843 CFMutableDictionaryRef escrowCopied = NULL;
1844 bool success = false;
1845
1846 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1847 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1848 else
1849 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1850
1851 CFDictionaryAddValue(escrowCopied, dsid, record);
1852 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1853
1854 if(*error == NULL)
1855 success = true;
1856
1857 CFReleaseNull(escrowCopied);
1858
1859 return success;
1860
1861 }
1862
1863 bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1864 bool success = false;
1865
1866 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1867 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1868
1869 return success;
1870 }
1871
1872 bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
1873 {
1874 CFStringRef operationString = NULL;
1875 CFDictionaryRef rawMessage = NULL;
1876 CFMutableSetRef peers = NULL;
1877 CFMutableDictionaryRef peerList = NULL;
1878 char* message = NULL;
1879 bool result = false;
1880
1881 if(account->ids_message_transport == NULL)
1882 account->ids_message_transport = (SOSTransportMessageRef)SOSTransportMessageIDSCreate(account, SOSCircleGetName(account->trusted_circle), error);
1883
1884 require_quiet(account->ids_message_transport, fail);
1885
1886 //adding message type kIDSPeerAvailability so KeychainSyncingOverIDSProxy does not send this message as a keychain item
1887
1888 operationString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSPeerAvailability);
1889 rawMessage = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1890 kIDSOperationType, operationString,
1891 kIDSMessageToSendKey, CFSTR("checking peers"),
1892 NULL);
1893
1894 peerList = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1895 SOSCircleRef circle = account->trusted_circle;
1896
1897 //check each peer to make sure they have the right view set enabled
1898 CFSetRef mySubSet = SOSViewsGetV0SubviewSet();
1899 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
1900 if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account))){
1901 CFMutableSetRef peerViews = SOSPeerInfoCopyEnabledViews(peer);
1902 CFSetRef intersectSets = CFSetCreateIntersection(kCFAllocatorDefault, mySubSet, peerViews);
1903 if(CFEqualSafe(intersectSets, mySubSet)){
1904 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
1905 if(deviceID != NULL)
1906 CFDictionaryAddValue(peerList, SOSPeerInfoGetPeerID(peer), rawMessage);
1907 CFReleaseNull(deviceID);
1908 }
1909 CFReleaseNull(peerViews);
1910 CFReleaseNull(intersectSets);
1911 }
1912 });
1913
1914 require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
1915 result = SOSTransportMessageSendMessages(account->ids_message_transport, peerList, error);
1916
1917 fail:
1918 CFReleaseNull(rawMessage);
1919 CFReleaseNull(operationString);
1920 CFReleaseNull(peerList);
1921 CFReleaseNull(peers);
1922 free(message);
1923 return result;
1924 }
1925
1926
1927 void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
1928 if (!SOSAccountIsInCircle(account, NULL))
1929 return;
1930
1931 SOSAccountModifyCircle(account, NULL, ^bool (SOSCircleRef circle) {
1932 __block bool updated = false;
1933 CFSetForEach(account->retirees, ^(CFTypeRef element){
1934 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1935
1936 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1937 updated = true;
1938 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1939 CFErrorRef cleanupError = NULL;
1940 if (!SOSAccountCleanupAfterPeer(account, RETIREMENT_FINALIZATION_SECONDS, circle, retiree, &cleanupError))
1941 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1942 CFReleaseSafe(cleanupError);
1943 }
1944 });
1945 return updated;
1946 });
1947 }
1948
1949
1950 static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error) {
1951 size_t total_payload = 0;
1952
1953 CFDataRef publicBytes = NULL;
1954 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
1955
1956 if (result != errSecSuccess) {
1957 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
1958 return 0;
1959 }
1960
1961 require_quiet(accumulate_size(&total_payload, der_sizeof_number(gencount, error)), errOut);
1962 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(publicBytes, error)), errOut);
1963 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(signature, error)), errOut);
1964 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
1965
1966 errOut:
1967 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
1968 return 0;
1969 }
1970
1971 static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
1972 CFDataRef publicBytes = NULL;
1973
1974 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
1975
1976 if (result != errSecSuccess) {
1977 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
1978 return NULL;
1979 }
1980
1981
1982 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
1983 der_encode_number(gencount, error, der,
1984 der_encode_data_or_null(publicBytes, error, der,
1985 der_encode_data_or_null(signature, error, der, der_end))));
1986 return der_end;
1987 }
1988
1989 static CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFAllocatorRef allocator, CFErrorRef *error)
1990 {
1991 return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
1992 return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
1993 });
1994 }
1995
1996 struct piggyBackBlob {
1997 SOSGenCountRef gencount;
1998 SecKeyRef pubKey;
1999 CFDataRef signature;
2000 };
2001
2002 static struct piggyBackBlob *SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator, CFErrorRef *error,
2003 const uint8_t** der_p, const uint8_t *der_end) {
2004 const uint8_t *sequence_end;
2005 struct piggyBackBlob *retval = NULL;
2006 SOSGenCountRef gencount = NULL;
2007 CFDataRef signature = NULL;
2008 CFDataRef publicBytes = NULL;
2009
2010 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
2011 require_action_quiet(sequence_end != NULL, errOut,
2012 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
2013 *der_p = der_decode_number(allocator, 0, &gencount, error, *der_p, sequence_end);
2014 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, der_end);
2015 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, der_end);
2016 require_action_quiet(*der_p && *der_p == der_end, errOut,
2017 SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
2018 retval = malloc(sizeof(struct piggyBackBlob));
2019 retval->gencount = gencount;
2020 retval->signature = signature;
2021 retval->pubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
2022
2023 errOut:
2024 if(!retval) {
2025 CFReleaseNull(gencount);
2026 CFReleaseNull(publicBytes);
2027 CFReleaseNull(signature);
2028 }
2029 return retval;
2030 }
2031
2032 static struct piggyBackBlob *SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator, CFDataRef blobData, CFErrorRef *error)
2033 {
2034 size_t size = CFDataGetLength(blobData);
2035 const uint8_t *der = CFDataGetBytePtr(blobData);
2036 struct piggyBackBlob *inflated = SOSPiggyBackBlobCreateFromDER(allocator, error, &der, der + size);
2037 return inflated;
2038 }
2039
2040
2041
2042 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccountRef account, CFErrorRef* error) {
2043 SOSPeerInfoRef applicant = NULL;
2044 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2045 if(!userKey) return false;
2046 require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), errOut);
2047 require(SOSFullPeerInfoPromoteToApplication(account->my_identity, userKey, error), errOut);
2048 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, (SOSFullPeerInfoGetPeerInfo(account->my_identity)), error);
2049 errOut:
2050 return applicant;
2051 }
2052
2053
2054 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccountRef account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2055 SOSGenCountRef gencount = NULL;
2056 CFDataRef signature = NULL;
2057 SecKeyRef ourKey = NULL;
2058
2059 CFDataRef pbblob = NULL;
2060
2061 secnotice("circleJoin", "Making circle joining blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2062
2063 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2064 require_quiet(userKey, errOut);
2065
2066 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2067 require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
2068
2069 {
2070 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
2071 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2072 require_quiet(ourKey, errOut);
2073 }
2074
2075 SOSCircleRef currentCircle = SOSAccountGetCircle(account, error);
2076 require_quiet(currentCircle, errOut);
2077
2078 SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2079 require_quiet(prunedCircle, errOut);
2080 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2081
2082 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2083
2084 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2085 require_quiet(signature, errOut);
2086
2087 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, kCFAllocatorDefault, error);
2088
2089 errOut:
2090 CFReleaseNull(gencount);
2091 CFReleaseNull(signature);
2092 CFReleaseNull(ourKey);
2093
2094 if(!pbblob) {
2095 secnotice("circleJoin", "Failed to make circle joining blob as sponsor %@", *error);
2096 }
2097
2098 return pbblob;
2099 }
2100
2101 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account, CFDataRef joiningBlob, CFErrorRef *error) {
2102 bool retval = false;
2103 SecKeyRef userKey = NULL;
2104 struct piggyBackBlob *pbb = NULL;
2105
2106 secnotice("circleJoin", "Joining circles through piggy-back (SOSAccountCopyCircleJoiningBlob)");
2107
2108 userKey = SOSAccountGetPrivateCredential(account, error);
2109 require_quiet(userKey, errOut);
2110 pbb = SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault, joiningBlob, error);
2111 require_quiet(pbb, errOut);
2112
2113 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2114
2115 retval = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef copyOfCurrent) {
2116 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2117 pbb->gencount,
2118 pbb->pubKey,
2119 pbb->signature,
2120 account->my_identity, error);;
2121
2122 });
2123
2124 errOut:
2125 if(pbb) {
2126 CFReleaseNull(pbb->gencount);
2127 CFReleaseNull(pbb->pubKey);
2128 CFReleaseNull(pbb->signature);
2129 free(pbb);
2130 }
2131 return retval;
2132 }
2133
2134 static char boolToChars(bool val, char truechar, char falsechar) {
2135 return val? truechar: falsechar;
2136 }
2137
2138 #define ACCOUNTLOGSTATE "accountLogState"
2139 void SOSAccountLogState(SOSAccountRef account) {
2140 bool hasPubKey = account->user_public != NULL;
2141 bool pubTrusted = account->user_public_trusted;
2142 bool hasPriv = account->_user_private != NULL;
2143 SOSCCStatus stat = SOSAccountGetCircleStatus(account, NULL);
2144 CFStringRef userPubKeyID = (account->user_public) ? SOSCopyIDOfKeyWithLength(account->user_public, 8, NULL):
2145 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2146
2147 secnotice(ACCOUNTLOGSTATE, "Start");
2148
2149 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2150 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2151 userPubKeyID,
2152 SOSAccountGetSOSCCStatusString(stat)
2153 );
2154 CFReleaseNull(userPubKeyID);
2155 if(account->trusted_circle) SOSCircleLogState(ACCOUNTLOGSTATE, account->trusted_circle, account->user_public, SOSAccountGetMyPeerID(account));
2156 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2157 }
2158
2159 void SOSAccountLogViewState(SOSAccountRef account) {
2160 bool isInCircle = SOSAccountIsInCircle(account, NULL);
2161 require_quiet(isInCircle, imOut);
2162 SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
2163 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2164 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2165
2166 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2167 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2168 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2169 boolToChars(isInitialComplete, 'I', 'i'),
2170 boolToChars(isBackupComplete, 'B', 'b'),
2171 description);
2172 });
2173 CFReleaseNull(views);
2174 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2175 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2176 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2177 });
2178 CFReleaseNull(unsyncedViews);
2179
2180 imOut:
2181 secnotice(ACCOUNTLOGSTATE, "Finish");
2182
2183 return;
2184 }
2185
2186
2187 void SOSAccountSetTestSerialNumber(SOSAccountRef account, CFStringRef serial) {
2188 if(!isString(serial)) return;
2189 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2190 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2191 SOSAccountUpdateV2Dictionary(account, newv2dict);
2192 }