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