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