]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountTrustClassic.m
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountTrustClassic.m
1 //
2 // SOSAccountTrustClassic.m
3 // Security
4 //
5
6 #import <Foundation/Foundation.h>
7 #import "Security/SecureObjectSync/SOSAccount.h"
8 #import "Security/SecureObjectSync/SOSViews.h"
9 #import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
10 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
11 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
12 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
13 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
14
15 #import "Security/SecureObjectSync/SOSPeerInfoV2.h"
16 #import "Security/SecureObjectSync/SOSPeerInfoCollections.h"
17 #import "Security/SecureObjectSync/SOSTransportMessageKVS.h"
18
19 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
20 #include <Security/SecureObjectSync/SOSTransportCircleKVS.h>
21 #include <Security/SecureObjectSync/SOSTransportCircle.h>
22 #include <Security/SecureObjectSync/SOSCircleDer.h>
23 #include <Security/SecureObjectSync/SOSInternal.h>
24
25 #include <utilities/SecCFWrappers.h>
26 #include <utilities/SecCoreCrypto.h>
27 #include <utilities/SecBuffer.h>
28
29 @implementation SOSAccountTrustClassic
30 extern CFStringRef kSOSAccountDebugScope;
31
32 +(instancetype)trustClassic
33 {
34 return [[self alloc] init];
35 }
36
37 -(id)init
38 {
39 self = [super init];
40 if(self)
41 {
42 self.retirees = [NSMutableSet set];
43 self.fullPeerInfo = NULL;
44 self.trustedCircle = NULL;
45 self.departureCode = kSOSDepartureReasonError;
46 self.expansion = [NSMutableDictionary dictionary];
47 [self addRingDictionary];
48 }
49 return self;
50 }
51
52 -(id)initWithRetirees:(NSMutableSet*)r fpi:(SOSFullPeerInfoRef)fpi circle:(SOSCircleRef) trusted_circle
53 departureCode:(enum DepartureReason)code peerExpansion:(NSMutableDictionary*)e
54 {
55 self = [super init];
56 if(self)
57 {
58 self.retirees = [[NSMutableSet alloc] initWithSet:r] ;
59 self.fullPeerInfo = CFRetainSafe(fpi);
60 self.trustedCircle = CFRetainSafe(trusted_circle);
61 self.departureCode = code;
62 self.expansion = [[NSMutableDictionary alloc]initWithDictionary:e];
63
64 [self addRingDictionary];
65 }
66 return self;
67
68
69 }
70
71 -(bool) updateGestalt:(SOSAccount*)account newGestalt:(CFDictionaryRef)new_gestalt
72 {
73 if (CFEqualSafe(new_gestalt, (__bridge CFDictionaryRef)(account.gestalt)))
74 return false;
75
76 if (self.trustedCircle && self.fullPeerInfo
77 && SOSFullPeerInfoUpdateGestalt(self.fullPeerInfo, new_gestalt, NULL)) {
78 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
79 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
80 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
81 }];
82 }
83
84 account.gestalt = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary * _Nonnull)(new_gestalt)];
85 return true;
86 }
87
88 #pragma clang diagnostic push
89 #pragma clang diagnostic ignored "-Wunused-value"
90 -(SOSViewResultCode) updateView:(SOSAccount*)account name:(CFStringRef) viewname code:(SOSViewActionCode) actionCode err:(CFErrorRef *)error
91 {
92 SOSViewResultCode retval = kSOSCCGeneralViewError;
93 SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
94 bool alreadyInSync = SOSAccountHasCompletedInitialSync(account);
95
96 bool updateCircle = false;
97 require_action_quiet(self.trustedCircle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
98 require_action_quiet(self.fullPeerInfo, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
99 require_action_quiet((actionCode == kSOSCCViewEnable) || (actionCode == kSOSCCViewDisable), errOut, CFSTR("Invalid View Action"));
100 currentStatus = [account.trust viewStatus:account name:viewname err:error];
101 require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
102
103 if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
104 retval = SOSAccountVirtualV0Behavior(account, actionCode);
105 } else if ([account.trust isSyncingV0] && SOSViewsIsV0Subview(viewname)) {
106 // Subviews of V0 syncing can't be turned off if V0 is on.
107 require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
108 retval = kSOSCCViewMember;
109 } else {
110 CFMutableSetRef pendingSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
111 CFSetAddValue(pendingSet, viewname);
112
113 if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
114 if(alreadyInSync) {
115 retval = SOSFullPeerInfoUpdateViews(self.fullPeerInfo, actionCode, viewname, error);
116 if(retval == kSOSCCViewMember) updateCircle = true;
117 } else {
118 [self pendEnableViewSet:pendingSet];
119 retval = kSOSCCViewMember;
120 updateCircle = false;
121 }
122 } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
123 if(alreadyInSync) {
124 retval = SOSFullPeerInfoUpdateViews(self.fullPeerInfo, actionCode, viewname, error);
125 if(retval == kSOSCCViewNotMember) updateCircle = true;
126 } else {
127 SOSAccountPendDisableViewSet(account, pendingSet);
128 retval = kSOSCCViewNotMember;
129 updateCircle = false;
130 }
131 } else {
132 retval = currentStatus;
133 }
134 CFReleaseNull(pendingSet);
135
136 if (updateCircle) {
137 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
138 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
139 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
140 }];
141 }
142 }
143
144 errOut:
145 return retval;
146 }
147 #pragma clang diagnostic pop
148
149 -(bool) activeValidInCircle:(SOSAccount*) account err:(CFErrorRef *)error {
150 return SOSCircleHasActiveValidPeer(self.trustedCircle, SOSFullPeerInfoGetPeerInfo(self.fullPeerInfo), SOSAccountGetTrustedPublicCredential(account, error), error);
151 }
152
153 -(SOSViewResultCode) viewStatus:(SOSAccount*)account name:(CFStringRef) viewname err:(CFErrorRef *)error
154 {
155 SOSViewResultCode retval = kSOSCCGeneralViewError;
156
157 require_action_quiet(SOSAccountGetTrustedPublicCredential(account, error), errOut, SOSCreateError(kSOSErrorNoKey, CFSTR("No Trusted UserKey"), NULL, error));
158 require_action_quiet(self.trustedCircle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
159 require_action_quiet(self.fullPeerInfo, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
160 require_action_quiet([self activeValidInCircle: account err: error ],
161 errOut, SOSCreateError(kSOSErrorNotInCircle, CFSTR("Not in Circle"), NULL, error));
162
163 if ([self valueSetContainsValue:kSOSPendingEnableViewsToBeSetKey value:viewname]) {
164 retval = kSOSCCViewMember;
165 } else if ([self valueSetContainsValue:kSOSPendingDisableViewsToBeSetKey value:viewname]) {
166 retval = kSOSCCViewNotMember;
167 } else {
168 retval = SOSFullPeerInfoViewStatus(self.fullPeerInfo, viewname, error);
169 }
170
171 // 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
172 if (retval != kSOSCCViewMember) {
173 if ((CFEqualSafe(viewname, kSOSViewKeychainV0) || SOSViewsIsV0Subview(viewname))
174 && [account.trust isSyncingV0]) {
175 retval = kSOSCCViewMember;
176 }
177 }
178
179 // If we're only an applicant we report pending if we would be a view member
180 if (retval == kSOSCCViewMember) {
181 bool isApplicant = SOSCircleHasApplicant(self.trustedCircle, self.peerInfo, error);
182 if (isApplicant) {
183 retval = kSOSCCViewPending;
184 }
185 }
186
187 errOut:
188 return retval;
189 }
190
191 static void dumpViewSet(CFStringRef label, CFSetRef views) {
192 if(views) {
193 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
194 secnotice("circleChange", "%@ list: %@", label, description);
195 });
196 } else {
197 secnotice("circleChange", "No %@ list provided.", label);
198 }
199 }
200
201 static bool SOSAccountScreenViewListForValidV0(SOSAccount* account, CFMutableSetRef viewSet, SOSViewActionCode actionCode) {
202 bool retval = true;
203 if(viewSet && CFSetContainsValue(viewSet, kSOSViewKeychainV0)) {
204 retval = SOSAccountVirtualV0Behavior(account, actionCode) != kSOSCCGeneralViewError;
205 CFSetRemoveValue(viewSet, kSOSViewKeychainV0);
206 }
207 return retval;
208 }
209
210 -(bool) updateViewSetsWithAnalytics:(SOSAccount*)account enabled:(CFSetRef) origEnabledViews disabled:(CFSetRef) origDisabledViews parentEvent:(NSData*)parentEvent
211 {
212 bool retval = false;
213 bool updateCircle = false;
214 SOSPeerInfoRef pi = NULL;
215 NSError* localError = nil;
216 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
217
218 SFSignInAnalytics *hasCompletedInitialSyncEvent = nil;
219 SFSignInAnalytics *updatePeerInCircleEvent = nil;
220
221 CFMutableSetRef enabledViews = NULL;
222 CFMutableSetRef disabledViews = NULL;
223 if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
224 if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
225 dumpViewSet(CFSTR("Enabled"), enabledViews);
226 dumpViewSet(CFSTR("Disabled"), disabledViews);
227
228 require_action_quiet(self.trustedCircle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
229
230 // Make sure we have a peerInfo capable of supporting views.
231 SOSFullPeerInfoRef fpi = self.fullPeerInfo;
232 require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
233 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
234
235 pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
236
237 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
238
239 if(!SOSPeerInfoVersionIsCurrent(pi)) {
240 CFErrorRef updateFailure = NULL;
241 require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
242 (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
243 secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
244 updateCircle = true;
245 }
246
247 CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
248 secnotice("viewChange", "Enabling %@", description);
249 });
250
251 CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
252 secnotice("viewChange", "Disabling %@", description);
253 });
254
255 require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
256 require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
257
258 hasCompletedInitialSyncEvent = [parent newSubTaskForEvent:@"hasCompletedInitialSyncEvent"];
259 if(SOSAccountHasCompletedInitialSync(account)) {
260 if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
261 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
262 retval = true;
263 } else {
264 //hold on to the views and enable them later
265 if(enabledViews) [self pendEnableViewSet:enabledViews];
266 if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
267 retval = true;
268 }
269 [hasCompletedInitialSyncEvent stopWithAttributes:nil];
270 if(updateCircle) {
271 /* UPDATE FULLPEERINFO VIEWS */
272 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
273 updatePeerInCircleEvent = [parent newSubTaskForEvent:@"updatePeerInCircleEvent"];
274 require_quiet([self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
275 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
276 bool updated= SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
277 return updated;
278 }], errOut);
279 [updatePeerInCircleEvent stopWithAttributes:nil];
280 // Make sure we update the engine
281 account.circle_rings_retirements_need_attention = true;
282 }
283
284 errOut:
285 [updatePeerInCircleEvent stopWithAttributes:nil];
286 CFReleaseNull(enabledViews);
287 CFReleaseNull(disabledViews);
288 CFReleaseNull(pi);
289 return retval;
290 }
291 -(bool) updateViewSets:(SOSAccount*)account enabled:(CFSetRef) origEnabledViews disabled:(CFSetRef) origDisabledViews
292 {
293 bool retval = false;
294 bool updateCircle = false;
295 SOSPeerInfoRef pi = NULL;
296 CFMutableSetRef enabledViews = NULL;
297 CFMutableSetRef disabledViews = NULL;
298 if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
299 if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
300 dumpViewSet(CFSTR("Enabled"), enabledViews);
301 dumpViewSet(CFSTR("Disabled"), disabledViews);
302
303 require_action_quiet(self.trustedCircle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
304
305 // Make sure we have a peerInfo capable of supporting views.
306 SOSFullPeerInfoRef fpi = self.fullPeerInfo;
307 require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
308 require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
309
310 pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
311
312 require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
313
314 if(!SOSPeerInfoVersionIsCurrent(pi)) {
315 CFErrorRef updateFailure = NULL;
316 require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
317 (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
318 secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
319 updateCircle = true;
320 }
321
322 CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
323 secnotice("viewChange", "Enabling %@", description);
324 });
325
326 CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
327 secnotice("viewChange", "Disabling %@", description);
328 });
329
330 require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
331 require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
332
333 if(SOSAccountHasCompletedInitialSync(account)) {
334 if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
335 if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
336 retval = true;
337 } else {
338 //hold on to the views and enable them later
339 if(enabledViews) [self pendEnableViewSet:enabledViews];
340 if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
341 retval = true;
342 }
343
344 if(updateCircle) {
345 /* UPDATE FULLPEERINFO VIEWS */
346 require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
347
348 require_quiet([self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
349 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
350 bool updated= SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
351 return updated;
352 }], errOut);
353
354 // Make sure we update the engine
355 account.circle_rings_retirements_need_attention = true;
356 }
357
358 errOut:
359 CFReleaseNull(enabledViews);
360 CFReleaseNull(disabledViews);
361 CFReleaseNull(pi);
362 return retval;
363 }
364
365
366 static inline void CFArrayAppendValueIfNot(CFMutableArrayRef array, CFTypeRef value, CFTypeRef excludedValue)
367 {
368 if (!CFEqualSafe(value, excludedValue))
369 CFArrayAppendValue(array, value);
370 }
371
372 -(void) addSyncablePeerBlock:(SOSAccountTransaction*)txn dsName:(CFStringRef) ds_name change:(SOSAccountSyncablePeersBlock) changeBlock
373 {
374 if (!changeBlock) return;
375 SOSAccount* account = txn.account;
376 CFRetainSafe(ds_name);
377 SOSAccountCircleMembershipChangeBlock block_to_register = ^void (SOSAccount *account, SOSCircleRef new_circle,
378 CFSetRef added_peers, CFSetRef removed_peers,
379 CFSetRef added_applicants, CFSetRef removed_applicants) {
380
381 if (!CFEqualSafe(SOSCircleGetName(new_circle), ds_name))
382 return;
383
384 SOSPeerInfoRef myPi = self.peerInfo;
385 CFStringRef myPi_id = myPi ? SOSPeerInfoGetPeerID(myPi) : NULL;
386
387 CFMutableArrayRef peer_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
388 CFMutableArrayRef added_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
389 CFMutableArrayRef removed_ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
390
391 if (SOSCircleHasPeer(new_circle, myPi, NULL)) {
392 SOSCircleForEachPeer(new_circle, ^(SOSPeerInfoRef peer) {
393 CFArrayAppendValueIfNot(peer_ids, SOSPeerInfoGetPeerID(peer), myPi_id);
394 });
395
396 CFSetForEach(added_peers, ^(const void *value) {
397 CFArrayAppendValueIfNot(added_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
398 });
399
400 CFSetForEach(removed_peers, ^(const void *value) {
401 CFArrayAppendValueIfNot(removed_ids, SOSPeerInfoGetPeerID((SOSPeerInfoRef) value), myPi_id);
402 });
403 }
404
405 if (CFArrayGetCount(peer_ids) || CFSetContainsValue(removed_peers, myPi))
406 changeBlock(peer_ids, added_ids, removed_ids);
407
408 CFReleaseSafe(peer_ids);
409 CFReleaseSafe(added_ids);
410 CFReleaseSafe(removed_ids);
411 };
412
413 SOSAccountAddChangeBlock(account, block_to_register);
414
415 CFSetRef empty = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
416 if (self.trustedCircle && CFEqualSafe(ds_name, SOSCircleGetName(self.trustedCircle))) {
417 block_to_register(account, self.trustedCircle, empty, empty, empty, empty);
418 }
419 CFReleaseSafe(empty);
420 }
421
422
423 -(CFSetRef) copyPeerSetForView:(CFStringRef) viewName
424 {
425 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
426
427 if (self.trustedCircle) {
428 SOSCircleForEachPeer(self.trustedCircle, ^(SOSPeerInfoRef peer) {
429 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer), viewName)) {
430 CFSetAddValue(result, peer);
431 }
432 });
433 }
434
435 return result;
436 }
437
438 -(SecKeyRef) copyPublicKeyForPeer:(CFStringRef) peer_id err:(CFErrorRef *)error
439 {
440 SecKeyRef publicKey = NULL;
441 SOSPeerInfoRef peer = NULL;
442
443 require_action_quiet(self.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to get peer key from")));
444
445 peer = SOSCircleCopyPeerWithID(self.trustedCircle, peer_id, error);
446 require_quiet(peer, fail);
447
448 publicKey = SOSPeerInfoCopyPubKey(peer, error);
449
450 fail:
451 CFReleaseSafe(peer);
452 return publicKey;
453 }
454
455 //Peers
456 -(SOSPeerInfoRef) copyPeerWithID:(CFStringRef) peerid err:(CFErrorRef *)error
457 {
458 if(!self.trustedCircle) return NULL;
459 return SOSCircleCopyPeerWithID(self.trustedCircle, peerid, error);
460 }
461 -(bool) isAccountIdentity:(SOSPeerInfoRef)peerInfo err:(CFErrorRef *)error
462 {
463 return CFEqualSafe(peerInfo, self.peerInfo);
464 }
465
466 -(CFSetRef) copyPeerSetMatching:(SOSModifyPeerBlock)block
467 {
468 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
469 if (self.trustedCircle) {
470 SOSCircleForEachPeer(self.trustedCircle, ^(SOSPeerInfoRef peer) {
471 if (block(peer)) {
472 CFSetAddValue(result, peer);
473 }
474 });
475 }
476
477 return result;
478 }
479 -(CFArrayRef) copyPeersToListenTo:(SecKeyRef)userPublic err:(CFErrorRef *)error
480 {
481 SOSPeerInfoRef myPeerInfo = self.peerInfo;
482 CFStringRef myID = myPeerInfo ? SOSPeerInfoGetPeerID(myPeerInfo) : NULL;
483 return [self copySortedPeerArray:error action:^(SOSCircleRef circle, CFMutableArrayRef appendPeersTo) {
484 SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) {
485 if(!CFEqualSafe(myID, SOSPeerInfoGetPeerID(peer)) &&
486 SOSPeerInfoApplicationVerify(peer, userPublic, NULL) &&
487 !SOSPeerInfoIsRetirementTicket(peer)) {
488 CFArrayAppendValue(appendPeersTo, peer);
489 }
490 });
491 }];
492 }
493 -(bool) peerSignatureUpdate:(SecKeyRef)privKey err:(CFErrorRef *)error
494 {
495 return self.fullPeerInfo && SOSFullPeerInfoUpgradeSignatures(self.fullPeerInfo, privKey, error);
496 }
497 -(bool) updatePeerInfo:(SOSKVSCircleStorageTransport*)circleTransport description:(CFStringRef)updateDescription err:(CFErrorRef *)error update:(SOSModifyPeerInfoBlock)block
498 {
499 if (self.fullPeerInfo == NULL)
500 return true;
501
502 bool result = block(self.fullPeerInfo, error);
503
504 if (result && [self hasCircle:NULL]) {
505 return [self modifyCircle:circleTransport err:error action:^(SOSCircleRef circle_to_change) {
506 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription);
507 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
508 }];
509 }
510
511 return result;
512 }
513
514 //Views
515 -(void) removeInvalidApplications:(SOSCircleRef) circle userPublic:(SecKeyRef)userPublic
516 {
517 CFMutableSetRef peersToRemove = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
518 SOSCircleForEachApplicant(circle, ^(SOSPeerInfoRef peer) {
519 if (!SOSPeerInfoApplicationVerify(peer, userPublic, NULL))
520 CFSetAddValue(peersToRemove, peer);
521 });
522
523 CFSetForEach(peersToRemove, ^(const void *value) {
524 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
525
526 SOSCircleWithdrawRequest(circle, peer, NULL);
527 });
528 }
529
530 -(bool) upgradeiCloudIdentity:(SOSCircleRef) circle privKey:(SecKeyRef) privKey
531 {
532 bool retval = false;
533 SOSFullPeerInfoRef cloud_fpi = SOSCircleCopyiCloudFullPeerInfoRef(circle, NULL);
534 require_quiet(cloud_fpi != NULL, errOut);
535 require_quiet(SOSFullPeerInfoUpgradeSignatures(cloud_fpi, privKey, NULL), errOut);
536 retval = SOSCircleUpdatePeerInfo(circle, SOSFullPeerInfoGetPeerInfo(cloud_fpi));
537 errOut:
538 CFReleaseNull(cloud_fpi);
539 return retval;
540 }
541 const CFStringRef kSOSHsaPreApprovedPeerKeyInfo = CFSTR("HSAPreApprovedPeer");
542
543 -(CFMutableSetRef) copyPreApprovedHSA2Info
544 {
545 CFMutableSetRef preApprovedPeers = (CFMutableSetRef) [self getValueFromExpansion:kSOSHsaPreApprovedPeerKeyInfo err:NULL];
546
547 if(preApprovedPeers) {
548 preApprovedPeers = CFSetCreateMutableCopy(NULL, 0, preApprovedPeers);
549 } else {
550 preApprovedPeers = CFSetCreateMutableForCFTypes(NULL);
551 }
552 return preApprovedPeers;
553 }
554
555
556 -(bool) addiCloudIdentity:(SOSCircleRef) circle key:(SecKeyRef) userKey err:(CFErrorRef*)error
557 {
558 bool result = false;
559 SOSFullPeerInfoRef cloud_identity = NULL;
560 SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
561 if(!cloud_peer)
562 return result;
563 cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
564 CFReleaseNull(cloud_peer);
565 if(!cloud_identity)
566 return result;
567 if(!SOSCircleRequestAdmission(circle, userKey, cloud_identity, error)) {
568 CFReleaseNull(cloud_identity);
569 return result;
570 }
571
572 require_quiet(SOSCircleAcceptRequest(circle, userKey, self.fullPeerInfo, SOSFullPeerInfoGetPeerInfo(cloud_identity), error), err_out);
573 result = true;
574 err_out:
575 CFReleaseNull(cloud_identity);
576 return result;
577 }
578 -(bool) addEscrowToPeerInfo:(SOSFullPeerInfoRef) myPeer err:(CFErrorRef *)error
579 {
580 bool success = false;
581
582 CFDictionaryRef escrowRecords = [self getValueFromExpansion:kSOSEscrowRecord err:error];
583 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
584
585 return success;
586 }
587
588 -(CFArrayRef) copySortedPeerArray:(CFErrorRef *)error
589 action:(SOSModifyPeersInCircleBlock)block
590 {
591 CFMutableArrayRef peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
592
593 block(self.trustedCircle, peers);
594
595 CFArrayOfSOSPeerInfosSortByID(peers);
596
597 return peers;
598
599 }
600
601 #define CURRENT_ACCOUNT_PERSISTENT_VERSION 8
602
603 static size_t der_sizeof_data_optional(CFDataRef data)
604 {
605 return data ? der_sizeof_data(data, NULL) : 0;
606
607 }
608 -(size_t) getDEREncodedSize:(SOSAccount*)account err:(NSError**)error
609 {
610 size_t sequence_size = 0;
611 uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
612 CFErrorRef failure = NULL;
613
614 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail);
615 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary((__bridge CFDictionaryRef)account.gestalt, &failure)), fail);
616 require_quiet(accumulate_size(&sequence_size, SOSCircleGetDEREncodedSize(self.trustedCircle, &failure)), fail);
617 require_quiet(accumulate_size(&sequence_size, der_sizeof_fullpeer_or_null(self.fullPeerInfo, &failure)), fail);
618 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(self.departureCode)), fail);
619 require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account.accountKeyIsTrusted, &failure)), fail);
620 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account.accountKey, &failure)), fail);
621 require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account.previousAccountKey, &failure)), fail);
622 require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null((__bridge CFDataRef)account.accountKeyDerivationParamters, &failure)), fail);
623 require_quiet(accumulate_size(&sequence_size, SOSPeerInfoSetGetDEREncodedArraySize((__bridge CFSetRef)self.retirees, &failure)), fail);
624 accumulate_size(&sequence_size, der_sizeof_data_optional((__bridge CFDataRef)(account.backup_key)));
625 require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary((__bridge CFDictionaryRef)(self.expansion), &failure)), fail);
626
627 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size);
628
629 fail:
630 // Ensure some error is made, maybe not this one, tho.
631 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, &failure);
632 if (error) {
633 if (failure != NULL) {
634 *error = (__bridge_transfer NSError*) failure;
635 failure = NULL;
636 }
637 }
638 CFReleaseNull(failure);
639
640 return 0;
641 }
642
643 static uint8_t* der_encode_data_optional(CFDataRef data, CFErrorRef *error,
644 const uint8_t *der, uint8_t *der_end)
645 {
646 return data ? der_encode_data(data, error, der, der_end) : der_end;
647
648 }
649
650 -(uint8_t*) encodeToDER:(SOSAccount*)account err:(NSError**) error start:(const uint8_t*) der end:(uint8_t*)der_end
651 {
652 CFErrorRef failure = NULL;
653 uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION;
654 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
655 ccder_encode_uint64(version, der,
656 der_encode_dictionary((__bridge CFDictionaryRef)account.gestalt, &failure, der,
657 SOSCircleEncodeToDER(self.trustedCircle, &failure, der,
658 der_encode_fullpeer_or_null(self.fullPeerInfo, &failure, der,
659 ccder_encode_uint64(self.departureCode, der,
660 ccder_encode_bool(account.accountKeyIsTrusted, der,
661 der_encode_public_bytes(account.accountKey, &failure, der,
662 der_encode_public_bytes(account.previousAccountKey, &failure, der,
663 der_encode_data_or_null((__bridge CFDataRef)(account.accountKeyDerivationParamters), &failure, der,
664 SOSPeerInfoSetEncodeToArrayDER((__bridge CFSetRef)(self.retirees), &failure, der,
665 der_encode_data_optional((__bridge CFDataRef)account.backup_key, &failure, der,
666 der_encode_dictionary((__bridge CFDictionaryRef)(self.expansion), &failure, der,
667 der_end)))))))))))));
668
669 if (error) {
670 if (failure != NULL) {
671 *error = (__bridge_transfer NSError*) failure;
672 failure = NULL;
673 }
674 }
675 CFReleaseNull(failure);
676
677 return der_end;
678 }
679
680 -(CFMutableSetRef) CF_RETURNS_RETAINED syncWithPeers:(SOSAccountTransaction*) txn peerIDs:(CFSetRef) /* CFStringRef */ peerIDs err:(CFErrorRef *)error
681 {
682 CFMutableSetRef notMePeers = NULL;
683 CFMutableSetRef handledPeerIDs = NULL;
684 CFMutableSetRef peersForKVS = NULL;
685
686 SOSAccount* account = txn.account;
687
688 if(![account isInCircle:error])
689 {
690 handledPeerIDs = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
691 CFReleaseNull(notMePeers);
692 CFReleaseNull(peersForKVS);
693 return handledPeerIDs;
694 }
695
696 handledPeerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
697 peersForKVS = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
698
699 SOSPeerInfoRef myPeerInfo = account.peerInfo;
700 if(!myPeerInfo)
701 {
702 CFReleaseNull(notMePeers);
703 CFReleaseNull(peersForKVS);
704 return handledPeerIDs;
705
706 }
707 CFStringRef myPeerID = SOSPeerInfoGetPeerID(myPeerInfo);
708
709 notMePeers = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, peerIDs);
710 CFSetRemoveValue(notMePeers, myPeerID);
711
712 CFSetForEach(notMePeers, ^(const void *value) {
713 CFErrorRef localError = NULL;
714 CFStringRef peerID = asString(value, &localError);
715 SOSPeerInfoRef peerInfo = NULL;
716 require_quiet(peerID, skip);
717
718 peerInfo = SOSCircleCopyPeerWithID(self.trustedCircle, peerID, NULL);
719 if (peerInfo && SOSCircleHasValidSyncingPeer(self.trustedCircle, peerInfo, account.accountKey, NULL)) {
720 CFSetAddValue(peersForKVS, peerID);
721 } else {
722 CFSetAddValue(handledPeerIDs, peerID);
723 }
724
725 skip:
726 CFReleaseNull(peerInfo);
727 if (localError) {
728 secnotice("sync-with-peers", "Skipped peer ID: %@ due to %@", peerID, localError);
729 }
730 CFReleaseNull(localError);
731 });
732
733 CFSetRef handledKVSPeerIDs = SOSAccountSyncWithPeersOverKVS(txn, peersForKVS);
734 CFSetUnion(handledPeerIDs, handledKVSPeerIDs);
735 CFReleaseNull(handledKVSPeerIDs);
736
737 SOSAccountConsiderLoggingEngineState(txn);
738
739 CFReleaseNull(notMePeers);
740 CFReleaseNull(peersForKVS);
741 return handledPeerIDs;
742 }
743
744 -(bool) requestSyncWithAllPeers:(SOSAccountTransaction*) txn key:(SecKeyRef)userPublic err:(CFErrorRef *)error
745 {
746 if (![txn.account isInCircle: error]) {
747 return false;
748 }
749
750 NSMutableSet<NSString*>* allSyncingPeerIDs = [NSMutableSet set];
751
752 SOSCircleForEachValidSyncingPeer(self.trustedCircle, userPublic, ^(SOSPeerInfoRef peer) {
753 [allSyncingPeerIDs addObject: (__bridge NSString*) SOSPeerInfoGetPeerID(peer)];
754 });
755
756 [txn requestSyncWithPeers: allSyncingPeerIDs];
757
758 return true;
759 }
760
761 -(bool) isSyncingV0{
762 __block bool syncingV0 = false;
763
764 [self forEachCirclePeerExceptMe:^(SOSPeerInfoRef peer){
765 if (SOSPeerInfoIsEnabledView(peer, kSOSViewKeychainV0)) {
766 syncingV0 = true;
767 }
768 }];
769
770 return syncingV0;
771 }
772
773 -(SOSEngineRef) getDataSourceEngine:(SOSDataSourceFactoryRef)factory
774 {
775 return SOSDataSourceFactoryGetEngineForDataSourceName(factory, SOSCircleGetName(self.trustedCircle), NULL);
776 }
777
778 -(bool) postDebugScope:(SOSKVSCircleStorageTransport*) circle_transport scope:(CFTypeRef) scope err:(CFErrorRef*)error
779 {
780 bool result = false;
781 if (circle_transport) {
782 result = [circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
783 }
784 return result;
785 }
786
787 -(SecKeyRef) copyDeviceKey:(CFErrorRef *)error
788 {
789 SecKeyRef privateKey = NULL;
790
791 require_action_quiet(self.fullPeerInfo, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No identity to get key from")));
792
793 privateKey = SOSFullPeerInfoCopyDeviceKey(self.fullPeerInfo, error);
794
795 fail:
796 return privateKey;
797
798 }
799 -(bool) removeIncompleteiCloudIdentities:(SOSCircleRef) circle privKey:(SecKeyRef) privKey err:(CFErrorRef *)error
800 {
801 bool retval = false;
802
803 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
804
805 SOSCircleForEachActivePeer(self.trustedCircle, ^(SOSPeerInfoRef peer) {
806 if(SOSPeerInfoIsCloudIdentity(peer)) {
807 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
808 if(!icfpi) {
809 CFSetAddValue(iCloud2Remove, peer);
810 }
811 CFReleaseNull(icfpi);
812 }
813 });
814
815 if(CFSetGetCount(iCloud2Remove) > 0) {
816 retval = true;
817 SOSCircleRemovePeers(self.trustedCircle, privKey, self.fullPeerInfo, iCloud2Remove, error);
818 }
819 CFReleaseNull(iCloud2Remove);
820 return retval;
821 }
822
823 -(bool) clientPing:(SOSAccount*)account
824 {
825 if (self.trustedCircle && self.fullPeerInfo
826 && SOSFullPeerInfoPing(self.fullPeerInfo, NULL)) {
827 [self modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle_to_change) {
828 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
829 return SOSCircleUpdatePeerInfo(circle_to_change, self.peerInfo);
830 }];
831 }
832
833 return true;
834 }
835 static NSString* kSOSRingKey = @"trusted_rings";
836
837 -(void) addRingDictionary {
838
839 if(self.expansion) {
840 if(![self.expansion valueForKey:kSOSRingKey]) {
841 NSMutableDictionary *rings = [NSMutableDictionary dictionary];
842 [self.expansion setObject:rings forKey:kSOSRingKey];
843 }
844 }
845 }
846
847 @end
848