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