]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.m
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.m
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 */
4
5 /*
6 * SOSAccount.c - Implementation of the secure object syncing account.
7 * An account contains a SOSCircle for each protection domain synced.
8 */
9
10 #import <Foundation/Foundation.h>
11
12 #include <Security/SecureObjectSync/SOSAccount.h>
13 #include <Security/SecureObjectSync/SOSPeerInfo.h>
14 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
15 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
16 #import "Security/SecureObjectSync/SOSTransportCircle.h"
17 #import "Security/SecureObjectSync/SOSTransportCircleKVS.h"
18 #include <Security/SecureObjectSync/SOSTransportMessage.h>
19 #include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
20 #include <Security/SecureObjectSync/SOSTransportKeyParameter.h>
21 #include <Security/SecureObjectSync/SOSKVSKeys.h>
22 #include <Security/SecureObjectSync/SOSTransport.h>
23 #include <Security/SecureObjectSync/SOSPeerCoder.h>
24 #include <Security/SecureObjectSync/SOSInternal.h>
25 #include <Security/SecureObjectSync/SOSRing.h>
26 #include <Security/SecureObjectSync/SOSRingUtils.h>
27 #include <Security/SecureObjectSync/SOSRingRecovery.h>
28 #include <Security/SecureObjectSync/SOSAccountTransaction.h>
29 #include <Security/SecureObjectSync/SOSAccountGhost.h>
30 #include <Security/SecureObjectSync/SOSPiggyback.h>
31 #include <Security/SecureObjectSync/SOSControlHelper.h>
32
33 #import <Security/SecureObjectSync/SOSAccountTrust.h>
34 #import <Security/SecureObjectSync/SOSAccountTrustClassic.h>
35 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h"
36 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h"
37 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Identity.h"
38 #import "Security/SecureObjectSync/SOSAccountTrustClassic+Retirement.h"
39 #import "Security/SecureObjectSync/SOSPeerOTRTimer.h"
40 #import "Security/SecureObjectSync/SOSPeerRateLimiter.h"
41 #import "Security/SecureObjectSync/SOSTypes.h"
42 #if OCTAGON
43 #import "keychain/ckks/CKKSViewManager.h"
44 #import "keychain/ckks/CKKSLockStateTracker.h"
45 #import "keychain/ot/OTContext.h"
46 #endif
47 #include <Security/SecItemInternal.h>
48 #include <Security/SecEntitlements.h>
49 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
50
51 #include <SecItemServer.h>
52
53 #import "SecdWatchdog.h"
54
55 #include <utilities/SecCFWrappers.h>
56 #include <utilities/SecCFError.h>
57 #include <utilities/SecADWrapper.h>
58
59 #include <os/activity.h>
60 #include <os/state_private.h>
61
62 #include <utilities/SecCoreCrypto.h>
63
64 #include <utilities/der_plist.h>
65 #include <utilities/der_plist_internal.h>
66 #include <corecrypto/ccder.h>
67
68 const CFStringRef kSOSAccountName = CFSTR("AccountName");
69 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
70 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
71 const CFStringRef kSOSInitialSyncTimeoutV0 = CFSTR("initialsynctimeout");
72 const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
73 const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
74 const CFStringRef kSOSTestV2Settings = CFSTR("v2dictionary");
75 const CFStringRef kSOSRecoveryKey = CFSTR("RecoveryKey");
76 const CFStringRef kSOSRecoveryRing = CFSTR("RecoveryRing");
77 const CFStringRef kSOSAccountUUID = CFSTR("UUID");
78 const CFStringRef kSOSRateLimitingCounters = CFSTR("RateLimitCounters");
79 const CFStringRef kSOSAccountPeerNegotiationTimeouts = CFSTR("PeerNegotiationTimeouts"); //Dictionary<CFStringRef, CFNumberRef>
80 const CFStringRef kSOSAccountPeerLastSentTimestamp = CFSTR("kSOSAccountPeerLastSentTimestamp"); //Dictionary<CFStringRef, CFDateRef>
81 const CFStringRef kSOSAccountRenegotiationRetryCount = CFSTR("NegotiationRetryCount");
82 const CFStringRef kOTRConfigVersion = CFSTR("OTRConfigVersion");
83 NSString* const SecSOSAggdReattemptOTRNegotiation = @"com.apple.security.sos.otrretry";
84 NSString* const SOSAccountUserDefaultsSuite = @"com.apple.security.sosaccount";
85 NSString* const SOSAccountLastKVSCleanup = @"lastKVSCleanup";
86
87 const uint64_t max_packet_size_over_idms = 500;
88
89
90 #define kPublicKeyNotAvailable "com.apple.security.publickeynotavailable"
91
92 #define DATE_LENGTH 25
93 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
94
95 @implementation SOSAccount
96
97 // Auto synthesis for most fields works great.
98 // A few CF fields need retention work when assigning.
99
100 -(id) init
101 {
102 self = [super init];
103 if(self){
104 self.gestalt = [NSMutableDictionary dictionary];
105 self.backup_key = nil;
106 self.deviceID = nil;
107
108 self.trust = [SOSAccountTrustClassic trustClassic];
109
110 self.queue = NULL;
111 self.user_private_timer = NULL;
112 self.factory = NULL;
113
114 self._password_tmp = nil;
115 self.isListeningForSync = false;
116 self.lock_notification_token = -1;
117
118 self.circle_transport = NULL;
119
120 self.circle_rings_retirements_need_attention = false;
121 self.engine_peer_state_needs_repair = false;
122 self.key_interests_need_updating = false;
123
124 self.change_blocks = [NSMutableArray array];
125
126 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
127 self.isInitialSyncing = false;
128
129 self.accountKeyIsTrusted = false;
130 self.accountKeyDerivationParamters = nil;
131
132 self.accountKey = NULL;
133 self.accountPrivateKey = NULL;
134 self.previousAccountKey = NULL;
135
136 self.saveBlock = nil;
137
138 self.notifyCircleChangeOnExit = false;
139 self.notifyViewChangeOnExit = false;
140 self.notifyBackupOnExit = false;
141
142 self.settings = [[NSUserDefaults alloc] initWithSuiteName:SOSAccountUserDefaultsSuite];
143 }
144 return self;
145 }
146
147 - (void)dealloc {
148 if(self) {
149 CFReleaseNull(self->_accountKey);
150 CFReleaseNull(self->_accountPrivateKey);
151 CFReleaseNull(self->_previousAccountKey);
152 }
153 }
154
155 @synthesize accountKey = _accountKey;
156
157 - (void) setAccountKey: (SecKeyRef) key {
158 CFRetainAssign(self->_accountKey, key);
159 }
160
161 @synthesize previousAccountKey = _previousAccountKey;
162
163 - (void) setPreviousAccountKey: (SecKeyRef) key {
164 CFRetainAssign(self->_previousAccountKey, key);
165 }
166
167 @synthesize accountPrivateKey = _accountPrivateKey;
168
169 - (void) setAccountPrivateKey: (SecKeyRef) key {
170 CFRetainAssign(self->_accountPrivateKey, key);
171 }
172
173 // Syntactic sugar getters
174
175 - (BOOL) hasPeerInfo {
176 return self.fullPeerInfo != nil;
177 }
178
179 - (SOSPeerInfoRef) peerInfo {
180 return self.trust.peerInfo;
181 }
182
183 - (SOSFullPeerInfoRef) fullPeerInfo {
184 return self.trust.fullPeerInfo;
185 }
186
187 - (NSString*) peerID {
188 return self.trust.peerID;
189 }
190
191 -(bool) ensureFactoryCircles
192 {
193 if (!self){
194 return false;
195 }
196
197 if (!self.factory){
198 return false;
199 }
200
201 NSString* circle_name = (__bridge_transfer NSString*)SOSDataSourceFactoryCopyName(self.factory);
202
203 if (!circle_name){
204 return false;
205 }
206
207 CFReleaseSafe(SOSAccountEnsureCircle(self, (__bridge CFStringRef) circle_name, NULL));
208
209 return SOSAccountInflateTransports(self, (__bridge CFStringRef) circle_name, NULL);
210 }
211
212 -(void)ensureOctagonPeerKeys
213 {
214 #if OCTAGON
215 CKKSLockStateTracker *tracker = [CKKSViewManager manager].lockStateTracker;
216 if (tracker && tracker.isLocked == false) {
217 [self.trust ensureOctagonPeerKeys:self.circle_transport];
218 }
219 #endif
220 }
221
222 -(id) initWithGestalt:(CFDictionaryRef)newGestalt factory:(SOSDataSourceFactoryRef)f
223 {
224 self = [super init];
225 if(self){
226 self.queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
227
228 self.gestalt = [[NSDictionary alloc] initWithDictionary:(__bridge NSDictionary * _Nonnull)(newGestalt)];
229
230 SOSAccountTrustClassic *t = [[SOSAccountTrustClassic alloc] initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
231
232 self.trust = t;
233 self.factory = f; // We adopt the factory. kthanksbai.
234
235 self.isListeningForSync = false;
236
237 self.accountPrivateKey = NULL;
238 self._password_tmp = NULL;
239 self.user_private_timer = NULL;
240 self.lock_notification_token = NOTIFY_TOKEN_INVALID;
241
242 self.change_blocks = [NSMutableArray array];
243 self.waitForInitialSync_blocks = NULL;
244
245 self.key_transport = nil;
246 self.circle_transport = NULL;
247 self.ck_storage = nil;
248 self.kvs_message_transport = nil;
249
250 self.circle_rings_retirements_need_attention = false;
251 self.engine_peer_state_needs_repair = false;
252 self.key_interests_need_updating = false;
253
254 self.backup_key =nil;
255 self.deviceID = nil;
256
257 self.waitForInitialSync_blocks = [NSMutableDictionary dictionary];
258 self.isInitialSyncing = false;
259 self.accountKeyIsTrusted = false;
260 self.accountKeyDerivationParamters = NULL;
261 self.accountKey = NULL;
262 self.previousAccountKey = NULL;
263
264 self.saveBlock = nil;
265 }
266 return self;
267 }
268
269 -(BOOL)isEqual:(id) object
270 {
271 if(![object isKindOfClass:[SOSAccount class]])
272 return NO;
273
274 SOSAccount* left = object;
275 return ([self.gestalt isEqual: left.gestalt] &&
276 CFEqualSafe(self.trust.trustedCircle, left.trust.trustedCircle) &&
277 [self.trust.expansion isEqual: left.trust.expansion] &&
278 CFEqualSafe(self.trust.fullPeerInfo, left.trust.fullPeerInfo));
279
280 }
281
282 - (void)userPublicKey:(void ((^))(BOOL trusted, NSData *spki, NSError *error))reply
283 {
284 dispatch_async(self.queue, ^{
285 if (!self.accountKeyIsTrusted || self.accountKey == NULL) {
286 NSDictionary *userinfo = @{
287 (id)kCFErrorDescriptionKey : @"User public key not trusted",
288 };
289 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
290 return;
291 }
292
293 NSData *data = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(self.accountKey));
294 if (data == NULL) {
295 NSDictionary *userinfo = @{
296 (id)kCFErrorDescriptionKey : @"User public not available",
297 };
298 reply(self.accountKeyIsTrusted, NULL, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSErrorPublicKeyAbsent userInfo:userinfo]);
299 return;
300 }
301 reply(self.accountKeyIsTrusted, data, NULL);
302 });
303 }
304
305 - (void)kvsPerformanceCounters:(void(^)(NSDictionary <NSString *, NSNumber *> *))reply
306 {
307 /* Need to collect performance counters from all subsystems, not just circle transport, don't have counters yet though */
308 SOSCloudKeychainRequestPerfCounters(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error)
309 {
310 reply((__bridge NSDictionary *)returnedValues);
311 });
312 }
313
314 - (void)rateLimitingPerformanceCounters:(void(^)(NSDictionary <NSString *, NSString *> *))reply
315 {
316 CFErrorRef error = NULL;
317 CFDictionaryRef rateLimitingCounters = (CFDictionaryRef)SOSAccountGetValue(self, kSOSRateLimitingCounters, &error);
318 reply((__bridge NSDictionary*)rateLimitingCounters ? (__bridge NSDictionary*)rateLimitingCounters : [NSDictionary dictionary]);
319 }
320
321 - (void)stashedCredentialPublicKey:(void(^)(NSData *, NSError *error))reply
322 {
323 dispatch_async(self.queue, ^{
324 CFErrorRef error = NULL;
325
326 SecKeyRef user_private = SOSAccountCopyStashedUserPrivateKey(self, &error);
327 if (user_private == NULL) {
328 reply(NULL, (__bridge NSError *)error);
329 CFReleaseNull(error);
330 return;
331 }
332
333 NSData *publicKey = CFBridgingRelease(SecKeyCopySubjectPublicKeyInfo(user_private));
334 CFReleaseNull(user_private);
335 reply(publicKey, NULL);
336 });
337 }
338
339 - (void)assertStashedAccountCredential:(void(^)(BOOL result, NSError *error))complete
340 {
341 dispatch_async(self.queue, ^{
342 CFErrorRef error = NULL;
343 bool result = SOSAccountAssertStashedAccountCredential(self, &error);
344 complete(result, (__bridge NSError *)error);
345 CFReleaseNull(error);
346 });
347 }
348
349 static bool SyncKVSAndWait(CFErrorRef *error) {
350 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
351
352 __block bool success = false;
353
354 secnoticeq("fresh", "EFP calling SOSCloudKeychainSynchronizeAndWait");
355
356 os_activity_initiate("CloudCircle EFRESH", OS_ACTIVITY_FLAG_DEFAULT, ^(void) {
357 SOSCloudKeychainSynchronizeAndWait(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(__unused CFDictionaryRef returnedValues, CFErrorRef sync_error) {
358 secnotice("fresh", "EFP returned, callback error: %@", sync_error);
359
360 success = (sync_error == NULL);
361 if (error) {
362 CFRetainAssign(*error, sync_error);
363 }
364
365 dispatch_semaphore_signal(wait_for);
366 });
367
368
369 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
370 secnotice("fresh", "EFP complete: %s %@", success ? "success" : "failure", error ? *error : NULL);
371 });
372
373 return success;
374 }
375
376 static bool Flush(CFErrorRef *error) {
377 __block bool success = false;
378
379 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
380 secnotice("flush", "Starting");
381
382 SOSCloudKeychainFlush(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
383 success = (sync_error == NULL);
384 if (error) {
385 CFRetainAssign(*error, sync_error);
386 }
387
388 dispatch_semaphore_signal(wait_for);
389 });
390
391 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
392
393 secnotice("flush", "Returned %s", success? "Success": "Failure");
394
395 return success;
396 }
397
398 - (bool)syncWaitAndFlush:(CFErrorRef *)error
399 {
400 secnotice("pairing", "sync and wait starting");
401
402 if (!SyncKVSAndWait(error)) {
403 secnotice("pairing", "failed sync and wait: %@", error ? *error : NULL);
404 return false;
405 }
406 if (!Flush(error)) {
407 secnotice("pairing", "failed flush: %@", error ? *error : NULL);
408 return false;
409 }
410 secnotice("pairing", "finished sync and wait");
411 return true;
412 }
413
414 - (void)validatedStashedAccountCredential:(void(^)(NSData *credential, NSError *error))complete
415 {
416 CFErrorRef syncerror = NULL;
417
418 if (![self syncWaitAndFlush:&syncerror]) {
419 complete(NULL, (__bridge NSError *)syncerror);
420 CFReleaseNull(syncerror);
421 return;
422 }
423
424 dispatch_async(self.queue, ^{
425 CFErrorRef error = NULL;
426 SecKeyRef key = NULL;
427 key = SOSAccountCopyStashedUserPrivateKey(self, &error);
428 if (key == NULL) {
429 secnotice("pairing", "no stashed credential");
430 complete(NULL, (__bridge NSError *)error);
431 CFReleaseNull(error);
432 return;
433 }
434
435 SecKeyRef publicKey = SecKeyCopyPublicKey(key);
436 if (publicKey) {
437 secnotice("pairing", "returning stash credential: %@", publicKey);
438 CFReleaseNull(publicKey);
439 }
440
441 NSData *keydata = CFBridgingRelease(SecKeyCopyExternalRepresentation(key, &error));
442 CFReleaseNull(key);
443 complete(keydata, (__bridge NSError *)error);
444 CFReleaseNull(error);
445 });
446 }
447
448 - (void)stashAccountCredential:(NSData *)credential complete:(void(^)(bool success, NSError *error))complete
449 {
450 CFErrorRef syncerror = NULL;
451
452 if (![self syncWaitAndFlush:&syncerror]) {
453 complete(NULL, (__bridge NSError *)syncerror);
454 CFReleaseNull(syncerror);
455 return;
456 }
457
458 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
459 SecKeyRef accountPrivateKey = NULL;
460 CFErrorRef error = NULL;
461 NSDictionary *attributes = @{
462 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
463 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
464 };
465
466 accountPrivateKey = SecKeyCreateWithData((__bridge CFDataRef)credential, (__bridge CFDictionaryRef)attributes, &error);
467 if (accountPrivateKey == NULL) {
468 complete(false, (__bridge NSError *)error);
469 secnotice("pairing", "SecKeyCreateWithData failed: %@", error);
470 CFReleaseNull(error);
471 return;
472 }
473
474 if (!SOSAccountTryUserPrivateKey(self, accountPrivateKey, &error)) {
475 CFReleaseNull(accountPrivateKey);
476 complete(false, (__bridge NSError *)error);
477 secnotice("pairing", "SOSAccountTryUserPrivateKey failed: %@", error);
478 CFReleaseNull(error);
479 return;
480 }
481
482 secnotice("pairing", "SOSAccountTryUserPrivateKey succeeded");
483
484 CFReleaseNull(accountPrivateKey);
485 complete(true, NULL);
486 }];
487
488 // This makes getting the private key the same as Asserting the password - we read all the other things
489 // that we just expressed interest in.
490 CFErrorRef error = NULL;
491 if (!Flush(&error)) {
492 secnotice("pairing", "failed final flush: %@", error ? error : NULL);
493 return;
494 }
495 CFReleaseNull(error);
496 }
497
498 - (void)myPeerInfo:(void (^)(NSData *, NSError *))complete
499 {
500 __block CFErrorRef localError = NULL;
501 __block NSData *applicationBlob = NULL;
502 SecAKSDoWhileUserBagLocked(&localError, ^{
503 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
504 SOSPeerInfoRef application = SOSAccountCopyApplication(txn.account, &localError);
505 if (application) {
506 applicationBlob = CFBridgingRelease(SOSPeerInfoCopyEncodedData(application, kCFAllocatorDefault, &localError));
507 CFReleaseNull(application);
508 }
509 }];
510 });
511 complete(applicationBlob, (__bridge NSError *)localError);
512 CFReleaseNull(localError);
513 }
514
515 - (void)circleJoiningBlob:(NSData *)applicant complete:(void (^)(NSData *blob, NSError *))complete
516 {
517 __block CFErrorRef localError = NULL;
518 __block NSData *blob = NULL;
519 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(NULL, &localError, (__bridge CFDataRef)applicant);
520 if (peer == NULL) {
521 complete(NULL, (__bridge NSError *)localError);
522 CFReleaseNull(localError);
523 return;
524 }
525 SecAKSDoWhileUserBagLocked(&localError, ^{
526 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
527 blob = CFBridgingRelease(SOSAccountCopyCircleJoiningBlob(txn.account, peer, &localError));
528 }];
529 });
530
531 CFReleaseNull(peer);
532
533 complete(blob, (__bridge NSError *)localError);
534 CFReleaseNull(localError);
535 }
536
537 - (void)joinCircleWithBlob:(NSData *)blob version:(PiggyBackProtocolVersion)version complete:(void (^)(bool success, NSError *))complete
538 {
539 __block CFErrorRef localError = NULL;
540 __block bool res = false;
541
542 SecAKSDoWhileUserBagLocked(&localError, ^{
543 [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
544 res = SOSAccountJoinWithCircleJoiningBlob(txn.account, (__bridge CFDataRef)blob, version, &localError);
545 }];
546 });
547
548 complete(res, (__bridge NSError *)localError);
549 CFReleaseNull(localError);
550 }
551
552 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
553 {
554 CFErrorRef error = NULL;
555 uint32_t isflags = 0;
556
557 if (flags & SOSControlInitialSyncFlagTLK)
558 isflags |= SecServerInitialSyncCredentialFlagTLK;
559 if (flags & SOSControlInitialSyncFlagPCS)
560 isflags |= SecServerInitialSyncCredentialFlagPCS;
561 if (flags & SOSControlInitialSyncFlagPCSNonCurrent)
562 isflags |= SecServerInitialSyncCredentialFlagPCSNonCurrent;
563 if (flags & SOSControlInitialSyncFlagBluetoothMigration)
564 isflags |= SecServerInitialSyncCredentialFlagBluetoothMigration;
565
566
567 NSArray *array = CFBridgingRelease(_SecServerCopyInitialSyncCredentials(isflags, &error));
568 complete(array, (__bridge NSError *)error);
569 CFReleaseNull(error);
570 }
571
572 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
573 {
574 CFErrorRef error = NULL;
575 bool res = _SecServerImportInitialSyncCredentials((__bridge CFArrayRef)items, &error);
576 complete(res, (__bridge NSError *)error);
577 CFReleaseNull(error);
578 }
579
580 - (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
581 {
582 __block CFErrorRef localError = NULL;
583 __block bool res = false;
584
585 secnotice("sync", "trigger a forced sync for %@", peers);
586
587 SecAKSDoWhileUserBagLocked(&localError, ^{
588 [self performTransaction:^(SOSAccountTransaction *txn) {
589 if ([peers count]) {
590 NSSet *peersSet = [NSSet setWithArray:peers];
591 CFMutableSetRef handledPeers = SOSAccountSyncWithPeers(txn, (__bridge CFSetRef)peersSet, &localError);
592 if (handledPeers && CFSetGetCount(handledPeers) == (CFIndex)[peersSet count]) {
593 res = true;
594 }
595 CFReleaseNull(handledPeers);
596 } else {
597 res = SOSAccountRequestSyncWithAllPeers(txn, &localError);
598 }
599 }];
600 });
601 complete(res, (__bridge NSError *)localError);
602 CFReleaseNull(localError);
603 }
604
605 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
606 {
607 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
608 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
609 if (watchdogClass) {
610 NSDictionary* parameters = [[watchdogClass watchdog] watchdogParameters];
611 complete(parameters, nil);
612 }
613 else {
614 complete(nil, [NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
615 }
616 }
617
618 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete
619 {
620 // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
621 NSError* error = nil;
622 Class watchdogClass = NSClassFromString(@"SecdWatchdog");
623 if (watchdogClass) {
624 [[watchdogClass watchdog] setWatchdogParameters:parameters error:&error];
625 complete(error);
626 }
627 else {
628 complete([NSError errorWithDomain:@"com.apple.securityd.watchdog" code:1 userInfo:@{NSLocalizedDescriptionKey : @"failed to lookup SecdWatchdog class from ObjC runtime"}]);
629 }
630 }
631
632 //
633 // MARK: Save Block
634 //
635
636 - (void) flattenToSaveBlock {
637 if (self.saveBlock) {
638 NSError* error = nil;
639 NSData* saveData = [self encodedData:&error];
640
641 (self.saveBlock)((__bridge CFDataRef) saveData, (__bridge CFErrorRef) error);
642 }
643 }
644
645 CFDictionaryRef SOSAccountCopyGestalt(SOSAccount* account) {
646 return CFDictionaryCreateCopy(kCFAllocatorDefault, (__bridge CFDictionaryRef)account.gestalt);
647 }
648
649 CFDictionaryRef SOSAccountCopyV2Dictionary(SOSAccount* account) {
650 CFDictionaryRef v2dict = SOSAccountGetValue(account, kSOSTestV2Settings, NULL);
651 return CFDictionaryCreateCopy(kCFAllocatorDefault, v2dict);
652 }
653
654 static bool SOSAccountUpdateDSID(SOSAccount* account, CFStringRef dsid){
655 SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
656 //send new DSID over account changed
657 [account.circle_transport kvsSendOfficialDSID:dsid err:NULL];
658 return true;
659 }
660
661 void SOSAccountAssertDSID(SOSAccount* account, CFStringRef dsid) {
662 CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
663 if(accountDSID == NULL) {
664 secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
665
666 SOSAccountUpdateDSID(account, dsid);
667 } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
668 secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
669
670 //DSID has changed, blast the account!
671 SOSAccountSetToNew(account);
672
673 //update DSID to the new DSID
674 SOSAccountUpdateDSID(account, dsid);
675 } else {
676 secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
677 }
678 }
679
680
681 void SOSAccountPendDisableViewSet(SOSAccount* account, CFSetRef disabledViews)
682 {
683 [account.trust valueUnionWith:kSOSPendingDisableViewsToBeSetKey valuesToUnion:disabledViews];
684 [account.trust valueSubtractFrom:kSOSPendingEnableViewsToBeSetKey valuesToSubtract:disabledViews];
685 }
686
687 #pragma clang diagnostic push
688 #pragma clang diagnostic ignored "-Wunused-value"
689 SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccount* account, SOSViewActionCode actionCode) {
690 SOSViewResultCode retval = kSOSCCGeneralViewError;
691 // The V0 view switches on and off all on it's own, we allow people the delusion
692 // of control and status if it's what we're stuck at., otherwise error.
693 if (SOSAccountSyncingV0(account)) {
694 require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
695 retval = kSOSCCViewMember;
696 } else {
697 require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
698 retval = kSOSCCViewNotMember;
699 }
700 errOut:
701 return retval;
702 }
703 #pragma clang diagnostic pop
704
705 SOSAccount* SOSAccountCreate(CFAllocatorRef allocator,
706 CFDictionaryRef gestalt,
707 SOSDataSourceFactoryRef factory) {
708
709 SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
710 [a ensureFactoryCircles];
711 SOSAccountEnsureUUID(a);
712 secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
713 a.key_interests_need_updating = true;
714
715 return a;
716 }
717
718 static OSStatus do_delete(CFDictionaryRef query) {
719 OSStatus result;
720
721 result = SecItemDelete(query);
722 if (result) {
723 secerror("SecItemDelete: %d", (int)result);
724 }
725 return result;
726 }
727
728 static int
729 do_keychain_delete_aks_bags()
730 {
731 OSStatus result;
732 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
733 kSecClass, kSecClassGenericPassword,
734 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
735 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
736 kSecAttrService, CFSTR("SecureBackupService"),
737 kSecAttrSynchronizable, kCFBooleanTrue,
738 kSecUseTombstones, kCFBooleanFalse,
739 NULL);
740
741 result = do_delete(item);
742 CFReleaseSafe(item);
743
744 return result;
745 }
746
747 static int
748 do_keychain_delete_identities()
749 {
750 OSStatus result;
751 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
752 kSecClass, kSecClassKey,
753 kSecAttrSynchronizable, kCFBooleanTrue,
754 kSecUseTombstones, kCFBooleanFalse,
755 kSecAttrAccessGroup, CFSTR("com.apple.security.sos"),
756 NULL);
757
758 result = do_delete(item);
759 CFReleaseSafe(item);
760
761 return result;
762 }
763
764 static int
765 do_keychain_delete_lakitu()
766 {
767 OSStatus result;
768 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
769 kSecClass, kSecClassGenericPassword,
770 kSecAttrSynchronizable, kCFBooleanTrue,
771 kSecUseTombstones, kCFBooleanFalse,
772 kSecAttrAccessGroup, CFSTR("com.apple.lakitu"),
773 kSecAttrAccount, CFSTR("EscrowServiceBypassToken"),
774 kSecAttrService, CFSTR("EscrowService"),
775 NULL);
776
777 result = do_delete(item);
778 CFReleaseSafe(item);
779
780 return result;
781 }
782
783 static int
784 do_keychain_delete_sbd()
785 {
786 OSStatus result;
787 CFDictionaryRef item = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
788 kSecClass, kSecClassGenericPassword,
789 kSecAttrSynchronizable, kCFBooleanTrue,
790 kSecUseTombstones, kCFBooleanFalse,
791 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
792 NULL);
793
794 result = do_delete(item);
795 CFReleaseSafe(item);
796
797 return result;
798 }
799
800 void SOSAccountSetToNew(SOSAccount* a)
801 {
802 secnotice("accountChange", "Setting Account to New");
803 int result = 0;
804
805 /* remove all syncable items */
806 result = do_keychain_delete_aks_bags(); (void) result;
807 secdebug("set to new", "result for deleting aks bags: %d", result);
808
809 result = do_keychain_delete_identities(); (void) result;
810 secdebug("set to new", "result for deleting identities: %d", result);
811
812 result = do_keychain_delete_lakitu(); (void) result;
813 secdebug("set to new", "result for deleting lakitu: %d", result);
814
815 result = do_keychain_delete_sbd(); (void) result;
816 secdebug("set to new", "result for deleting sbd: %d", result);
817
818 a.accountKeyIsTrusted = false;
819
820 if (a.user_private_timer) {
821 dispatch_source_cancel(a.user_private_timer);
822 a.user_private_timer = NULL;
823 xpc_transaction_end();
824
825 }
826 if (a.lock_notification_token != NOTIFY_TOKEN_INVALID) {
827 notify_cancel(a.lock_notification_token);
828 a.lock_notification_token = NOTIFY_TOKEN_INVALID;
829 }
830
831 // keeping gestalt;
832 // keeping factory;
833 // Live Notification
834 // change_blocks;
835 // update_interest_block;
836 // update_block;
837 SOSUnregisterTransportKeyParameter(a.key_transport);
838 SOSUnregisterTransportMessage(a.kvs_message_transport);
839 SOSUnregisterTransportCircle(a.circle_transport);
840
841 a.circle_transport = NULL;
842 a.kvs_message_transport = nil;
843
844 a.trust = nil;
845 a.trust = [[SOSAccountTrustClassic alloc]initWithRetirees:[NSMutableSet set] fpi:NULL circle:NULL departureCode:kSOSDepartureReasonError peerExpansion:[NSMutableDictionary dictionary]];
846
847 [a ensureFactoryCircles]; // Does rings too
848
849 // By resetting our expansion dictionary we've reset our UUID, so we'll be notified properly
850 SOSAccountEnsureUUID(a);
851 secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountSetToNew");
852 a.key_interests_need_updating = true;
853 }
854
855 bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error){
856 bool result = false;
857 SOSAccountTrustClassic* trust = account.trust;
858 if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
859 CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
860 return result;
861
862 if(trust.retirees != nil)
863 return result;
864 if(trust.expansion != nil)
865 return result;
866
867 if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
868 return result;
869
870 result = true;
871
872 return result;
873 }
874
875 dispatch_queue_t SOSAccountGetQueue(SOSAccount* account) {
876 return account.queue;
877 }
878
879 void SOSAccountSetUserPublicTrustedForTesting(SOSAccount* account){
880 account.accountKeyIsTrusted = true;
881 }
882
883 -(SOSCCStatus) getCircleStatus:(CFErrorRef*) error
884 {
885 SOSCCStatus circleStatus = [self.trust getCircleStatusOnly:error];
886 if (!SOSAccountHasPublicKey(self, error)) {
887 if(circleStatus == kSOSCCInCircle) {
888 if(error) {
889 CFReleaseNull(*error);
890 SOSCreateError(kSOSErrorPublicKeyAbsent, CFSTR("Public Key isn't available, this peer is in the circle, but invalid. The iCloud Password must be provided to keychain syncing subsystem to repair this."), NULL, error);
891 }
892 }
893 circleStatus = kSOSCCError;
894 }
895 return circleStatus;
896 }
897
898 -(bool) isInCircle:(CFErrorRef *)error
899 {
900 SOSCCStatus result = [self getCircleStatus:error];
901 if (result != kSOSCCInCircle) {
902 SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
903 return false;
904 }
905 return true;
906 }
907
908
909 bool SOSAccountScanForRetired(SOSAccount* account, SOSCircleRef circle, CFErrorRef *error) {
910
911 SOSAccountTrustClassic *trust = account.trust;
912 NSMutableSet* retirees = trust.retirees;
913 SOSCircleForEachRetiredPeer(circle, ^(SOSPeerInfoRef peer) {
914 CFSetSetValue((__bridge CFMutableSetRef) retirees, peer);
915 CFErrorRef cleanupError = NULL;
916 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:peer err:&cleanupError]) {
917 secnotice("retirement", "Error cleaning up after peer, probably orphaned some stuff in KVS: (%@) – moving on", cleanupError);
918 }
919 CFReleaseSafe(cleanupError);
920 });
921 return true;
922 }
923
924 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount* account, SOSCircleRef starting_circle, CFErrorRef *error) {
925 SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
926 SOSPeerInfoRef me = account.peerInfo;
927 bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
928
929 SOSAccountTrustClassic *trust = account.trust;
930 NSMutableSet* retirees = trust.retirees;
931
932 if(!new_circle) return NULL;
933 __block bool workDone = false;
934 if (retirees) {
935 CFSetForEach((__bridge CFSetRef)retirees, ^(const void* value) {
936 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
937 if (isSOSPeerInfo(pi)) {
938 SOSCircleUpdatePeerInfo(new_circle, pi);
939 workDone = true;
940 }
941 });
942 }
943
944 if(workDone && SOSCircleCountPeers(new_circle) == 0) {
945 SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
946
947 if(iAmApplicant) {
948 if(userPrivKey) {
949 secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
950 if(!SOSCircleResetToOffering(new_circle, userPrivKey, account.fullPeerInfo, error) ||
951 ![account.trust addiCloudIdentity:new_circle key:userPrivKey err:error]){
952 CFReleaseNull(new_circle);
953 return NULL;
954 }
955 account.notifyBackupOnExit = true;
956 } else {
957 // Do nothing. We can't resetToOffering without a userPrivKey. If we were to resetToEmpty
958 // we won't push the result later in handleUpdateCircle. If we leave the circle as it is
959 // we have a chance to set things right with a SetCreds/Join sequence. This will cause
960 // handleUpdateCircle to return false.
961 CFReleaseNull(new_circle);
962 return NULL;
963 }
964 } else {
965 // This case is when we aren't an applicant and the circle is retirement-empty.
966 secnotice("circleOps", "Reset to empty with last retirement");
967 SOSCircleResetToEmpty(new_circle, NULL);
968 }
969 }
970
971 return new_circle;
972 }
973
974 //
975 // MARK: Circle Membership change notificaion
976 //
977
978 void SOSAccountAddChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
979 SOSAccountCircleMembershipChangeBlock copy = changeBlock;
980 [a.change_blocks addObject:copy];
981 }
982
983 void SOSAccountRemoveChangeBlock(SOSAccount* a, SOSAccountCircleMembershipChangeBlock changeBlock) {
984 [a.change_blocks removeObject:changeBlock];
985 }
986
987 void SOSAccountPurgeIdentity(SOSAccount* account) {
988 SOSAccountTrustClassic *trust = account.trust;
989 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
990
991 if (identity) {
992 // Purge private key but don't return error if we can't.
993 CFErrorRef purgeError = NULL;
994 if (!SOSFullPeerInfoPurgePersistentKey(identity, &purgeError)) {
995 secwarning("Couldn't purge persistent key for %@ [%@]", identity, purgeError);
996 }
997 CFReleaseNull(purgeError);
998
999 trust.fullPeerInfo = nil;
1000 }
1001 }
1002
1003 bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error) {
1004 SOSAccountTrustClassic *trust = account.trust;
1005 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1006 NSMutableSet* retirees = trust.retirees;
1007
1008 NSError* localError = nil;
1009 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentData error:&localError];
1010
1011 SOSFullPeerInfoRef fpi = identity;
1012 if(!fpi) return false;
1013
1014 CFErrorRef retiredError = NULL;
1015
1016 bool retval = false;
1017
1018 SFSignInAnalytics *promoteToRetiredEvent = [parent newSubTaskForEvent:@"promoteToRetiredEvent"];
1019 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &retiredError);
1020 if(retiredError){
1021 [promoteToRetiredEvent logRecoverableError:(__bridge NSError*)retiredError];
1022 secerror("SOSFullPeerInfoPromoteToRetiredAndCopy error: %@", retiredError);
1023 if(error){
1024 *error = retiredError;
1025 }else{
1026 CFReleaseNull(retiredError);
1027 }
1028 }
1029 [promoteToRetiredEvent stopWithAttributes:nil];
1030
1031 if (!retire_peer) {
1032 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1033 } else {
1034 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1035 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1036 // Remove our application if we have one.
1037 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1038 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1039 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1040 CFErrorRef cleanupError = NULL;
1041 SFSignInAnalytics *cleanupEvent = [parent newSubTaskForEvent:@"cleanupAfterPeerEvent"];
1042 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1043 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1044 }
1045 [cleanupEvent stopWithAttributes:nil];
1046 CFReleaseSafe(cleanupError);
1047 }
1048 }
1049
1050 // Store the retirement record locally.
1051 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1052
1053 trust.retirees = retirees;
1054
1055 // Write retirement to Transport
1056 CFErrorRef postError = NULL;
1057 SFSignInAnalytics *postRestirementEvent = [parent newSubTaskForEvent:@"postRestirementEvent"];
1058 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1059 [postRestirementEvent logRecoverableError:(__bridge NSError*)postError];
1060 secwarning("Couldn't post retirement (%@)", postError);
1061 }
1062 [postRestirementEvent stopWithAttributes:nil];
1063
1064 SFSignInAnalytics *flushChangesEvent = [parent newSubTaskForEvent:@"flushChangesEvent"];
1065
1066 if(![account.circle_transport flushChanges:&postError]){
1067 [flushChangesEvent logRecoverableError:(__bridge NSError*)postError];
1068 secwarning("Couldn't flush retirement data (%@)", postError);
1069 }
1070 [flushChangesEvent stopWithAttributes:nil];
1071 CFReleaseNull(postError);
1072 }
1073 SFSignInAnalytics *purgeIdentityEvent = [parent newSubTaskForEvent:@"purgeIdentityEvent"];
1074 SOSAccountPurgeIdentity(account);
1075 [purgeIdentityEvent stopWithAttributes:nil];
1076 retval = true;
1077
1078 CFReleaseNull(retire_peer);
1079 return retval;
1080 }
1081
1082 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error) {
1083 SOSAccountTrustClassic *trust = account.trust;
1084 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1085 NSMutableSet* retirees = trust.retirees;
1086
1087 SOSFullPeerInfoRef fpi = identity;
1088 if(!fpi) return false;
1089
1090 CFErrorRef localError = NULL;
1091
1092 bool retval = false;
1093
1094 SOSPeerInfoRef retire_peer = SOSFullPeerInfoPromoteToRetiredAndCopy(fpi, &localError);
1095 if (!retire_peer) {
1096 secerror("Create ticket failed for peer %@: %@", fpi, localError);
1097 } else {
1098 // See if we need to repost the circle we could either be an applicant or a peer already in the circle
1099 if(SOSCircleHasApplicant(circle, retire_peer, NULL)) {
1100 // Remove our application if we have one.
1101 SOSCircleWithdrawRequest(circle, retire_peer, NULL);
1102 } else if (SOSCircleHasPeer(circle, retire_peer, NULL)) {
1103 if (SOSCircleUpdatePeerInfo(circle, retire_peer)) {
1104 CFErrorRef cleanupError = NULL;
1105 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retire_peer err:&cleanupError]) {
1106 secerror("Error cleanup up after peer (%@): %@", retire_peer, cleanupError);
1107 }
1108 CFReleaseSafe(cleanupError);
1109 }
1110 }
1111
1112 // Store the retirement record locally.
1113 CFSetAddValue((__bridge CFMutableSetRef)retirees, retire_peer);
1114
1115 trust.retirees = retirees;
1116
1117 // Write retirement to Transport
1118 CFErrorRef postError = NULL;
1119 if(![account.circle_transport postRetirement:SOSCircleGetName(circle) peer:retire_peer err:&postError]){
1120 secwarning("Couldn't post retirement (%@)", postError);
1121 }
1122 if(![account.circle_transport flushChanges:&postError]){
1123 secwarning("Couldn't flush retirement data (%@)", postError);
1124 }
1125 CFReleaseNull(postError);
1126 }
1127
1128 SOSAccountPurgeIdentity(account);
1129
1130 retval = true;
1131
1132 CFReleaseNull(localError);
1133 CFReleaseNull(retire_peer);
1134 return retval;
1135 }
1136
1137 bool sosAccountLeaveRing(SOSAccount* account, SOSRingRef ring, CFErrorRef* error) {
1138 SOSAccountTrustClassic *trust = account.trust;
1139 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1140
1141 SOSFullPeerInfoRef fpi = identity;
1142 if(!fpi) return false;
1143 SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
1144 CFStringRef peerID = SOSPeerInfoGetPeerID(pi);
1145
1146 CFErrorRef localError = NULL;
1147
1148 bool retval = false;
1149 bool writeRing = false;
1150 bool writePeerInfo = false;
1151
1152 if(SOSRingHasPeerID(ring, peerID)) {
1153 writePeerInfo = true;
1154 }
1155
1156 if(writePeerInfo || writeRing) {
1157 SOSRingWithdraw(ring, NULL, fpi, error);
1158 }
1159
1160 if (writeRing) {
1161 CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
1162
1163 if (ring_data) {
1164 [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
1165 }
1166 CFReleaseNull(ring_data);
1167 }
1168 retval = true;
1169 CFReleaseNull(localError);
1170 return retval;
1171 }
1172
1173 bool SOSAccountPostDebugScope(SOSAccount* account, CFTypeRef scope, CFErrorRef *error) {
1174 bool result = false;
1175 if (account.circle_transport) {
1176 result = [account.circle_transport kvssendDebugInfo:kSOSAccountDebugScope debug:scope err:error];
1177 }
1178 return result;
1179 }
1180
1181 /*
1182 NSUbiquitousKeyValueStoreInitialSyncChange is only posted if there is any
1183 local value that has been overwritten by a distant value. If there is no
1184 conflict between the local and the distant values when doing the initial
1185 sync (e.g. if the cloud has no data stored or the client has not stored
1186 any data yet), you'll never see that notification.
1187
1188 NSUbiquitousKeyValueStoreInitialSyncChange implies an initial round trip
1189 with server but initial round trip with server does not imply
1190 NSUbiquitousKeyValueStoreInitialSyncChange.
1191 */
1192
1193
1194 //
1195 // MARK: Status summary
1196 //
1197
1198
1199 CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
1200 switch(status) {
1201 case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
1202 case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
1203 case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
1204 case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
1205 case kSOSCCError: return CFSTR("kSOSCCError");
1206 }
1207 return CFSTR("kSOSCCError");
1208 }
1209 SOSCCStatus SOSAccountGetSOSCCStatusFromString(CFStringRef status) {
1210 if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1211 return kSOSCCInCircle;
1212 } else if(CFEqualSafe(status, CFSTR("kSOSCCInCircle"))) {
1213 return kSOSCCInCircle;
1214 } else if(CFEqualSafe(status, CFSTR("kSOSCCNotInCircle"))) {
1215 return kSOSCCNotInCircle;
1216 } else if(CFEqualSafe(status, CFSTR("kSOSCCRequestPending"))) {
1217 return kSOSCCRequestPending;
1218 } else if(CFEqualSafe(status, CFSTR("kSOSCCCircleAbsent"))) {
1219 return kSOSCCCircleAbsent;
1220 } else if(CFEqualSafe(status, CFSTR("kSOSCCError"))) {
1221 return kSOSCCError;
1222 }
1223 return kSOSCCError;
1224 }
1225
1226 //
1227 // MARK: Account Reset Circles
1228 //
1229
1230 // This needs to be called within a [trust modifyCircle()] block
1231
1232
1233 bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount* account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
1234 bool retval = false;
1235
1236 SOSAccountTrustClassic *trust = account.trust;
1237 SOSFullPeerInfoRef identity = trust.fullPeerInfo;
1238
1239 CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
1240
1241 SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
1242 if(SOSPeerInfoIsCloudIdentity(peer)) {
1243 SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
1244 if(!icfpi) {
1245 CFSetAddValue(iCloud2Remove, peer);
1246 }
1247 CFReleaseNull(icfpi);
1248 }
1249 });
1250
1251 if(CFSetGetCount(iCloud2Remove) > 0) {
1252 retval = true;
1253 SOSCircleRemovePeers(circle, privKey, identity, iCloud2Remove, error);
1254 }
1255 CFReleaseNull(iCloud2Remove);
1256 return retval;
1257 }
1258
1259 //
1260 // MARK: start backups
1261 //
1262
1263
1264 bool SOSAccountEnsureInBackupRings(SOSAccount* account) {
1265 __block bool result = false;
1266 __block CFErrorRef error = NULL;
1267 secnotice("backup", "Ensuring in rings");
1268
1269 NSData *backupKey = nil;
1270
1271 if(!account.backup_key){
1272 result = true;
1273 return result;
1274 }
1275
1276 backupKey = (__bridge_transfer NSData*)SOSPeerInfoV2DictionaryCopyData(account.peerInfo, sBackupKeyKey);
1277
1278 bool updateBackupKey = ![backupKey isEqual:account.backup_key];
1279
1280 if(updateBackupKey) {
1281 result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
1282 return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
1283 });
1284 if (!result) {
1285 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1286 CFReleaseNull(error);
1287 return result;
1288 }
1289 }
1290 if(!account.backup_key)
1291 {
1292 if (!result) {
1293 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1294 }
1295 CFReleaseNull(error);
1296 return result;
1297 }
1298 if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
1299 if (!result) {
1300 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1301 }
1302 CFReleaseNull(error);
1303 return result;
1304 }
1305
1306 CFDataRef recoveryKeyBackFromRing = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, &error);
1307
1308 if(updateBackupKey || recoveryKeyBackFromRing) {
1309 // It's a good key, we're going with it. Stop backing up the old way.
1310 CFErrorRef localError = NULL;
1311 if (!SOSDeleteV0Keybag(&localError)) {
1312 secerror("Failed to delete v0 keybag: %@", localError);
1313 }
1314 CFReleaseNull(localError);
1315
1316 result = true;
1317
1318 // Setup backups the new way.
1319 SOSAccountForEachBackupView(account, ^(const void *value) {
1320 CFStringRef viewName = (CFStringRef)value;
1321 if(updateBackupKey || (recoveryKeyBackFromRing && !SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewName))) {
1322 result &= SOSAccountNewBKSBForView(account, viewName, &error);
1323 }
1324 });
1325 }
1326
1327 if (!result) {
1328 secnotice("backupkey", "Failed to setup backup public key: %@", error);
1329 }
1330 CFReleaseNull(error);
1331 return result;
1332 }
1333
1334 //
1335 // MARK: Recovery Public Key Functions
1336 //
1337
1338 bool SOSAccountRegisterRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1339 bool retval = SOSAccountSetRecoveryKey(txn.account, recovery_key, error);
1340 if(retval) secnotice("recovery", "successfully registered recovery public key");
1341 else secnotice("recovery", "could not register recovery public key: %@", *error);
1342 SOSClearErrorIfTrue(retval, error);
1343 return retval;
1344 }
1345
1346 bool SOSAccountClearRecoveryPublicKey(SOSAccountTransaction* txn, CFDataRef recovery_key, CFErrorRef *error){
1347 bool retval = SOSAccountRemoveRecoveryKey(txn.account, error);
1348 if(retval) secnotice("recovery", "RK Cleared");
1349 else secnotice("recovery", "Couldn't clear RK(%@)", *error);
1350 SOSClearErrorIfTrue(retval, error);
1351 return retval;
1352 }
1353
1354 CFDataRef SOSAccountCopyRecoveryPublicKey(SOSAccountTransaction* txn, CFErrorRef *error){
1355 CFDataRef result = NULL;
1356 result = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, txn.account, error);
1357 if(!result) secnotice("recovery", "Could not retrieve the recovery public key from the ring: %@", *error);
1358
1359 if (!isData(result)) {
1360 CFReleaseNull(result);
1361 }
1362 SOSClearErrorIfTrue(result != NULL, error);
1363
1364 return result;
1365 }
1366
1367 //
1368 // MARK: Joining
1369 //
1370
1371 static bool SOSAccountJoinCircleWithAnalytics(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1372 bool use_cloud_peer, NSData* parentEvent, CFErrorRef* error) {
1373 SOSAccount* account = aTxn.account;
1374 SOSAccountTrustClassic *trust = account.trust;
1375 __block bool result = false;
1376 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1377 SFSignInAnalytics *ensureFullPeerAvailableEvent = nil;
1378 NSError* localError = nil;
1379 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
1380
1381 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1382 ensureFullPeerAvailableEvent = [parent newSubTaskForEvent:@"ensureFullPeerAvailableEvent"];
1383 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1384 [ensureFullPeerAvailableEvent stopWithAttributes:nil];
1385
1386 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1387 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1388 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1389 // this also clears initial sync data
1390 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1391 } else {
1392 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1393 if (use_cloud_peer) {
1394 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1395 }
1396 SFSignInAnalytics *acceptApplicantEvent = [parent newSubTaskForEvent:@"acceptApplicantEvent"];
1397 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1398 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1399 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1400 trust.departureCode = kSOSNeverLeftCircle;
1401 if(result && cloud_full_peer) {
1402 CFErrorRef localError = NULL;
1403 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1404 require_quiet(cloudid, finish);
1405 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1406 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1407 finish:
1408 if (localError){
1409 [acceptApplicantEvent logRecoverableError:(__bridge NSError *)(localError)];
1410 secerror("Failed to join with cloud identity: %@", localError);
1411 CFReleaseNull(localError);
1412 }
1413 }
1414 return result;
1415 }];
1416 [acceptApplicantEvent stopWithAttributes:nil];
1417 if (use_cloud_peer) {
1418 SFSignInAnalytics *updateOutOfDateSyncViewsEvent = [acceptApplicantEvent newSubTaskForEvent:@"updateOutOfDateSyncViewsEvent"];
1419 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1420 [updateOutOfDateSyncViewsEvent stopWithAttributes:nil];
1421 }
1422 }
1423 fail:
1424 CFReleaseNull(cloud_full_peer);
1425 return result;
1426 }
1427
1428 static bool SOSAccountJoinCircle(SOSAccountTransaction* aTxn, SecKeyRef user_key,
1429 bool use_cloud_peer, CFErrorRef* error) {
1430 SOSAccount* account = aTxn.account;
1431 SOSAccountTrustClassic *trust = account.trust;
1432 __block bool result = false;
1433 __block SOSFullPeerInfoRef cloud_full_peer = NULL;
1434 require_action_quiet(trust.trustedCircle, fail, SOSCreateErrorWithFormat(kSOSErrorPeerNotFound, NULL, error, NULL, CFSTR("Don't have circle when joining???")));
1435 require_quiet([account.trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)account.gestalt deviceID:(__bridge CFStringRef)account.deviceID backupKey:(__bridge CFDataRef)account.backup_key err:error], fail);
1436 SOSFullPeerInfoRef myCirclePeer = trust.fullPeerInfo;
1437 if (SOSCircleCountPeers(trust.trustedCircle) == 0 || SOSAccountGhostResultsInReset(account)) {
1438 secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
1439 // this also clears initial sync data
1440 result = [account.trust resetCircleToOffering:aTxn userKey:user_key err:error];
1441 } else {
1442 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
1443 if (use_cloud_peer) {
1444 cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(trust.trustedCircle, NULL);
1445 }
1446 [account.trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef circle) {
1447 result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
1448 result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
1449 trust.departureCode = kSOSNeverLeftCircle;
1450 if(result && cloud_full_peer) {
1451 CFErrorRef localError = NULL;
1452 CFStringRef cloudid = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(cloud_full_peer));
1453 require_quiet(cloudid, finish);
1454 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
1455 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
1456 finish:
1457 if (localError){
1458 secerror("Failed to join with cloud identity: %@", localError);
1459 CFReleaseNull(localError);
1460 }
1461 }
1462 return result;
1463 }];
1464 if (use_cloud_peer) {
1465 SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
1466 }
1467 }
1468 fail:
1469 CFReleaseNull(cloud_full_peer);
1470 return result;
1471 }
1472
1473 static bool SOSAccountJoinCirclesWithAnalytics_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, NSData* parentEvent, CFErrorRef* error) {
1474 SOSAccount* account = aTxn.account;
1475 SOSAccountTrustClassic *trust = account.trust;
1476 bool success = false;
1477
1478 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1479 require_quiet(user_key, done); // Fail if we don't get one.
1480
1481 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1482
1483 if (trust.fullPeerInfo != NULL) {
1484 SOSPeerInfoRef myPeer = trust.peerInfo;
1485 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1486 require_quiet(!success, done);
1487
1488 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1489
1490 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1491 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1492
1493 trust.fullPeerInfo = NULL;
1494 }
1495 }
1496
1497 success = SOSAccountJoinCircleWithAnalytics(aTxn, user_key, use_cloud_identity, parentEvent, error);
1498
1499 require_quiet(success, done);
1500
1501 trust.departureCode = kSOSNeverLeftCircle;
1502
1503 done:
1504 return success;
1505 }
1506
1507 static bool SOSAccountJoinCircles_internal(SOSAccountTransaction* aTxn, bool use_cloud_identity, CFErrorRef* error) {
1508 SOSAccount* account = aTxn.account;
1509 SOSAccountTrustClassic *trust = account.trust;
1510 bool success = false;
1511
1512 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1513 require_quiet(user_key, done); // Fail if we don't get one.
1514
1515 require_action_quiet(trust.trustedCircle, done, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle to join")));
1516
1517 if (trust.fullPeerInfo != NULL) {
1518 SOSPeerInfoRef myPeer = trust.peerInfo;
1519 success = SOSCircleHasPeer(trust.trustedCircle, myPeer, NULL);
1520 require_quiet(!success, done);
1521
1522 SOSCircleRemoveRejectedPeer(trust.trustedCircle, myPeer, NULL); // If we were rejected we should remove it now.
1523
1524 if (!SOSCircleHasApplicant(trust.trustedCircle, myPeer, NULL)) {
1525 secerror("Resetting my peer (ID: %@) for circle '%@' during application", SOSPeerInfoGetPeerID(myPeer), SOSCircleGetName(trust.trustedCircle));
1526
1527 trust.fullPeerInfo = NULL;
1528 }
1529 }
1530
1531 success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
1532
1533 require_quiet(success, done);
1534
1535 trust.departureCode = kSOSNeverLeftCircle;
1536
1537 done:
1538 return success;
1539 }
1540
1541 bool SOSAccountJoinCirclesWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
1542 secnotice("circleOps", "Normal path circle join (SOSAccountJoinCirclesWithAnalytics)");
1543 return SOSAccountJoinCirclesWithAnalytics_internal(aTxn, false, parentEvent, error);
1544 }
1545
1546 bool SOSAccountJoinCircles(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1547 secnotice("circleOps", "Normal path circle join (SOSAccountJoinCircles)");
1548 return SOSAccountJoinCircles_internal(aTxn, false, error);
1549 }
1550
1551 bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransaction* aTxn, CFErrorRef* error) {
1552 secnotice("circleOps", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1553 return SOSAccountJoinCircles_internal(aTxn, true, error);
1554 }
1555
1556 bool SOSAccountJoinCirclesAfterRestoreWithAnalytics(SOSAccountTransaction* aTxn, NSData* parentEvent, CFErrorRef* error) {
1557 secnotice("circleOps", "Joining after restore (SOSAccountJoinCirclesAfterRestore)");
1558 return SOSAccountJoinCirclesWithAnalytics_internal(aTxn, true, parentEvent, error);
1559 }
1560
1561 bool SOSAccountRemovePeersFromCircle(SOSAccount* account, CFArrayRef peers, CFErrorRef* error)
1562 {
1563 bool result = false;
1564 CFMutableSetRef peersToRemove = NULL;
1565 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1566 if(!user_key){
1567 secnotice("circleOps", "Can't remove without userKey");
1568 return result;
1569 }
1570 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1571 SOSPeerInfoRef me = account.peerInfo;
1572 if(!(me_full && me))
1573 {
1574 secnotice("circleOps", "Can't remove without being active peer");
1575 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1576 return result;
1577 }
1578
1579 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1580
1581 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1582 if(!peersToRemove)
1583 {
1584 CFReleaseNull(peersToRemove);
1585 secnotice("circleOps", "No peerSet to remove");
1586 return result;
1587 }
1588
1589 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1590 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1591 CFSetRemoveValue(peersToRemove, me);
1592
1593 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1594 bool success = false;
1595
1596 if(CFSetGetCount(peersToRemove) != 0) {
1597 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1598 success = SOSAccountGenerationSignatureUpdate(account, error);
1599 } else success = true;
1600
1601 if (success && leaveCircle) {
1602 secnotice("circleOps", "Leaving circle by client request (SOSAccountRemovePeersFromCircle)");
1603 success = sosAccountLeaveCircle(account, circle, error);
1604 }
1605
1606 done:
1607 return success;
1608
1609 }];
1610
1611 if(result) {
1612 CFStringSetPerformWithDescription(peersToRemove, ^(CFStringRef description) {
1613 secnotice("circleOps", "Removed Peers from circle %@", description);
1614 });
1615 }
1616
1617 CFReleaseNull(peersToRemove);
1618 return result;
1619 }
1620
1621
1622 bool SOSAccountRemovePeersFromCircleWithAnalytics(SOSAccount* account, CFArrayRef peers, NSData* parentEvent, CFErrorRef* error)
1623 {
1624
1625 NSError* localError = nil;
1626 SFSignInAnalytics* parent = [NSKeyedUnarchiver unarchivedObjectOfClass:[SFSignInAnalytics class] fromData:parentEvent error:&localError];
1627
1628 bool result = false;
1629 CFMutableSetRef peersToRemove = NULL;
1630 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1631 if(!user_key){
1632 secnotice("circleOps", "Can't remove without userKey");
1633 return result;
1634 }
1635 SOSFullPeerInfoRef me_full = account.fullPeerInfo;
1636 SOSPeerInfoRef me = account.peerInfo;
1637 if(!(me_full && me))
1638 {
1639 secnotice("circleOps", "Can't remove without being active peer");
1640 SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("Can't remove without being active peer"));
1641 return result;
1642 }
1643
1644 result = true; // beyond this point failures would be rolled up in AccountModifyCircle.
1645
1646 peersToRemove = CFSetCreateMutableForSOSPeerInfosByIDWithArray(kCFAllocatorDefault, peers);
1647 if(!peersToRemove)
1648 {
1649 CFReleaseNull(peersToRemove);
1650 secnotice("circleOps", "No peerSet to remove");
1651 return result;
1652 }
1653
1654 // If we're one of the peers expected to leave - note that and then remove ourselves from the set (different handling).
1655 bool leaveCircle = CFSetContainsValue(peersToRemove, me);
1656 CFSetRemoveValue(peersToRemove, me);
1657
1658 result &= [account.trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1659 bool success = false;
1660
1661 if(CFSetGetCount(peersToRemove) != 0) {
1662 require_quiet(SOSCircleRemovePeers(circle, user_key, me_full, peersToRemove, error), done);
1663 SFSignInAnalytics *generationSignatureUpdateEvent = [parent newSubTaskForEvent:@"generationSignatureUpdateEvent"];
1664 success = SOSAccountGenerationSignatureUpdate(account, error);
1665 if(error && *error){
1666 NSError* signatureUpdateError = (__bridge NSError*)*error;
1667 [generationSignatureUpdateEvent logUnrecoverableError:signatureUpdateError];
1668 }
1669 [generationSignatureUpdateEvent stopWithAttributes:nil];
1670 } else success = true;
1671
1672 if (success && leaveCircle) {
1673 secnotice("circleOps", "Leaving circle by client request (SOSAccountRemovePeersFromCircle)");
1674 success = sosAccountLeaveCircleWithAnalytics(account, circle, parentEvent, error);
1675 }
1676
1677 done:
1678 return success;
1679
1680 }];
1681
1682 if(result) {
1683 CFStringSetPerformWithDescription(peersToRemove, ^(CFStringRef description) {
1684 secnotice("circleOps", "Removed Peers from circle %@", description);
1685 });
1686 }
1687
1688 CFReleaseNull(peersToRemove);
1689 return result;
1690 }
1691
1692 bool SOSAccountBail(SOSAccount* account, uint64_t limit_in_seconds, CFErrorRef* error) {
1693 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1694 dispatch_group_t group = dispatch_group_create();
1695 SOSAccountTrustClassic *trust = account.trust;
1696 __block bool result = false;
1697 secnotice("circle", "Attempting to leave circle - best effort - in %llu seconds\n", limit_in_seconds);
1698 // Add a task to the group
1699 dispatch_group_async(group, queue, ^{
1700 [trust modifyCircle:account.circle_transport err:error action:^(SOSCircleRef circle) {
1701 secnotice("circleOps", "Leaving circle by client request (Bail)");
1702 return sosAccountLeaveCircle(account, circle, error);
1703 }];
1704 });
1705 dispatch_time_t milestone = dispatch_time(DISPATCH_TIME_NOW, limit_in_seconds * NSEC_PER_SEC);
1706 dispatch_group_wait(group, milestone);
1707
1708 trust.departureCode = kSOSWithdrewMembership;
1709
1710 return result;
1711 }
1712
1713
1714 //
1715 // MARK: Application
1716 //
1717
1718 static void for_each_applicant_in_each_circle(SOSAccount* account, CFArrayRef peer_infos,
1719 bool (^action)(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer)) {
1720 SOSAccountTrustClassic *trust = account.trust;
1721
1722 SOSPeerInfoRef me = trust.peerInfo;
1723 CFErrorRef peer_error = NULL;
1724 if (trust.trustedCircle && me &&
1725 SOSCircleHasPeer(trust.trustedCircle, me, &peer_error)) {
1726 [account.trust modifyCircle:account.circle_transport err:NULL action:^(SOSCircleRef circle) {
1727 __block bool modified = false;
1728 CFArrayForEach(peer_infos, ^(const void *value) {
1729 SOSPeerInfoRef peer = (SOSPeerInfoRef) value;
1730 if (isSOSPeerInfo(peer) && SOSCircleHasApplicant(circle, peer, NULL)) {
1731 if (action(circle, trust.fullPeerInfo, peer)) {
1732 modified = true;
1733 }
1734 }
1735 });
1736 return modified;
1737 }];
1738 }
1739 if (peer_error)
1740 secerror("Got error in SOSCircleHasPeer: %@", peer_error);
1741 CFReleaseSafe(peer_error); // TODO: We should be accumulating errors here.
1742 }
1743
1744 bool SOSAccountAcceptApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1745 SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
1746 if (!user_key)
1747 return false;
1748
1749 __block int64_t acceptedPeers = 0;
1750
1751 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1752 bool accepted = SOSCircleAcceptRequest(circle, user_key, myCirclePeer, peer, error);
1753 if (accepted)
1754 acceptedPeers++;
1755 return accepted;
1756 });
1757
1758 if (acceptedPeers == CFArrayGetCount(applicants))
1759 return true;
1760 return false;
1761 }
1762
1763 bool SOSAccountRejectApplicants(SOSAccount* account, CFArrayRef applicants, CFErrorRef* error) {
1764 __block bool success = true;
1765 __block int64_t num_peers = 0;
1766
1767 for_each_applicant_in_each_circle(account, applicants, ^(SOSCircleRef circle, SOSFullPeerInfoRef myCirclePeer, SOSPeerInfoRef peer) {
1768 bool rejected = SOSCircleRejectRequest(circle, myCirclePeer, peer, error);
1769 if (!rejected)
1770 success = false;
1771 else
1772 num_peers = MAX(num_peers, SOSCircleCountPeers(circle));
1773 return rejected;
1774 });
1775
1776 return success;
1777 }
1778
1779
1780 CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount* account, CFErrorRef* error) {
1781 return CFSTR("We're compatible, go away");
1782 }
1783
1784 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount* account, CFErrorRef* error) {
1785 SOSAccountTrustClassic *trust = account.trust;
1786 return trust.departureCode;
1787 }
1788
1789 void SOSAccountSetLastDepartureReason(SOSAccount* account, enum DepartureReason reason) {
1790 SOSAccountTrustClassic *trust = account.trust;
1791 trust.departureCode = reason;
1792 }
1793
1794
1795 CFArrayRef SOSAccountCopyGeneration(SOSAccount* account, CFErrorRef *error) {
1796 CFArrayRef result = NULL;
1797 CFNumberRef generation = NULL;
1798 SOSAccountTrustClassic *trust = account.trust;
1799
1800 require_quiet(SOSAccountHasPublicKey(account, error), fail);
1801 require_action_quiet(trust.trustedCircle, fail, SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("No circle")));
1802
1803 generation = (CFNumberRef)SOSCircleGetGeneration(trust.trustedCircle);
1804 result = CFArrayCreateForCFTypes(kCFAllocatorDefault, generation, NULL);
1805
1806 fail:
1807 return result;
1808 }
1809
1810 bool SOSValidateUserPublic(SOSAccount* account, CFErrorRef *error) {
1811 if (!SOSAccountHasPublicKey(account, error))
1812 return NULL;
1813
1814 return account.accountKeyIsTrusted;
1815 }
1816
1817 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error) {
1818 // TODO: this result is never set or used
1819 bool result = true;
1820 SOSAccountTrustClassic *trust = account.trust;
1821
1822 secnotice("updates", "Ensuring peer registration.");
1823
1824 if(!trust) {
1825 secnotice("updates", "Failed to get trust object in Ensuring peer registration.");
1826 return result;
1827 }
1828
1829 if([account getCircleStatus: NULL] != kSOSCCInCircle) {
1830 return result;
1831 }
1832
1833 // If we are not in the circle, there is no point in setting up peers
1834 if(!SOSAccountIsMyPeerActive(account, NULL)) {
1835 return result;
1836 }
1837
1838 // This code only uses the SOSFullPeerInfoRef for two things:
1839 // - Finding out if this device is in the trusted circle
1840 // - Using the peerID for this device to see if the current peer is "me"
1841 // - It is used indirectly by passing trust.fullPeerInfo to SOSEngineInitializePeerCoder
1842
1843 CFStringRef my_id = SOSPeerInfoGetPeerID(trust.peerInfo);
1844
1845 SOSCircleForEachValidSyncingPeer(trust.trustedCircle, account.accountKey, ^(SOSPeerInfoRef peer) {
1846 if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
1847 CFErrorRef localError = NULL;
1848
1849 SOSEngineInitializePeerCoder((SOSEngineRef)[account.kvs_message_transport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
1850 if (localError)
1851 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
1852 CFReleaseSafe(localError);
1853 }
1854 });
1855
1856 return result;
1857 }
1858
1859 //
1860 // Value manipulation
1861 //
1862
1863 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error) {
1864 SOSAccountTrustClassic *trust = account.trust;
1865 if (!trust.expansion) {
1866 return NULL;
1867 }
1868 return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
1869 }
1870
1871 bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
1872 CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
1873 CFMutableDictionaryRef escrowCopied = NULL;
1874 bool success = false;
1875
1876 if(isDictionary(escrowRecords) && escrowRecords != NULL)
1877 escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
1878 else
1879 escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1880
1881 CFDictionaryAddValue(escrowCopied, dsid, record);
1882 SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
1883
1884 if(*error == NULL)
1885 success = true;
1886
1887 CFReleaseNull(escrowCopied);
1888
1889 return success;
1890
1891 }
1892
1893 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
1894 bool success = false;
1895
1896 CFDictionaryRef escrowRecords = SOSAccountGetValue(account, kSOSEscrowRecord, error);
1897 success = SOSFullPeerInfoReplaceEscrowRecords(myPeer, escrowRecords, error);
1898
1899 return success;
1900 }
1901
1902 void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
1903 if (![account isInCircle:NULL]) {
1904 return;
1905 }
1906 SOSAccountTrustClassic *trust = account.trust;
1907 [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
1908 __block bool updated = false;
1909 CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
1910 SOSPeerInfoRef retiree = asSOSPeerInfo(element);
1911
1912 if (retiree && SOSCircleUpdatePeerInfo(circle, retiree)) {
1913 updated = true;
1914 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
1915 CFErrorRef cleanupError = NULL;
1916 if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
1917 secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
1918 CFReleaseSafe(cleanupError);
1919 }
1920 });
1921 return updated;
1922 }];
1923 }
1924
1925 static const uint64_t maxTimeToWaitInSeconds = 30ull * NSEC_PER_SEC;
1926
1927 static CFDictionaryRef SOSAccountGetObjectsFromCloud(dispatch_queue_t processQueue, CFErrorRef *error)
1928 {
1929 __block CFTypeRef object = NULL;
1930
1931 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1932 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1933
1934 CloudKeychainReplyBlock replyBlock =
1935 ^ (CFDictionaryRef returnedValues, CFErrorRef error)
1936 {
1937 object = returnedValues;
1938 if (object)
1939 CFRetain(object);
1940 if (error)
1941 {
1942 secerror("SOSCloudKeychainGetObjectsFromCloud returned error: %@", error);
1943 }
1944 dispatch_semaphore_signal(waitSemaphore);
1945 };
1946
1947 SOSCloudKeychainGetAllObjectsFromCloud(processQueue, replyBlock);
1948
1949 dispatch_semaphore_wait(waitSemaphore, finishTime);
1950 if (object && (CFGetTypeID(object) == CFNullGetTypeID())) // return a NULL instead of a CFNull
1951 {
1952 CFRelease(object);
1953 object = NULL;
1954 }
1955 return asDictionary(object, NULL); // don't propogate "NULL is not a dictionary" errors
1956 }
1957
1958
1959 static void SOSAccountRemoveKVSKeys(SOSAccount* account, NSArray* keysToRemove, dispatch_queue_t processQueue)
1960 {
1961 CFStringRef uuid = SOSAccountCopyUUID(account);
1962 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1963 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1964
1965 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
1966 if (error){
1967 secerror("SOSCloudKeychainRemoveKeys returned error: %@", error);
1968 }
1969 dispatch_semaphore_signal(waitSemaphore);
1970 };
1971
1972 SOSCloudKeychainRemoveKeys((__bridge CFArrayRef)(keysToRemove), uuid, processQueue, replyBlock);
1973 dispatch_semaphore_wait(waitSemaphore, finishTime);
1974 CFReleaseNull(uuid);
1975 }
1976
1977 static void SOSAccountWriteLastCleanupTimestampToKVS(SOSAccount* account)
1978 {
1979 NSDate *now = [NSDate date];
1980 [account.settings setObject:now forKey:SOSAccountLastKVSCleanup];
1981
1982 NSMutableDictionary *writeTimestamp = [NSMutableDictionary dictionary];
1983
1984 CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
1985
1986 CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
1987
1988 withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
1989 CFStringAppend(timeDescription, decription);
1990 });
1991 CFStringAppend(timeDescription, CFSTR("]"));
1992
1993 [writeTimestamp setObject:(__bridge NSString*)(timeDescription) forKey:(__bridge NSString*)kSOSKVSLastCleanupTimestampKey];
1994 CFReleaseNull(timeDescription);
1995
1996 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1997 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
1998 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1999
2000 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2001 if (error){
2002 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2003 }
2004 dispatch_semaphore_signal(waitSemaphore);
2005 };
2006
2007 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(writeTimestamp), processQueue, replyBlock);
2008 dispatch_semaphore_wait(waitSemaphore, finishTime);
2009 }
2010
2011 // set the cleanup frequency to 3 days.
2012 #define KVS_CLEANUP_FREQUENCY_LIMIT 60*60*24*3
2013
2014 //Get all the key/values in KVS and remove old entries
2015 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
2016 {
2017 // This should only happen on some number of days
2018 NSDate *lastKVSCleanup = [account.settings objectForKey:SOSAccountLastKVSCleanup];
2019 NSDate *now = [NSDate date];
2020 NSTimeInterval timeSinceCleanup = [now timeIntervalSinceDate:lastKVSCleanup];
2021
2022 if(timeSinceCleanup < KVS_CLEANUP_FREQUENCY_LIMIT) {
2023 return true;
2024 }
2025
2026 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2027
2028 NSDictionary *keysAndValues = (__bridge_transfer NSDictionary*)SOSAccountGetObjectsFromCloud(processQueue, error);
2029 NSMutableArray *peerIDs = [NSMutableArray array];
2030 NSMutableArray *keysToRemove = [NSMutableArray array];
2031
2032 CFArrayRef peers = SOSAccountCopyActiveValidPeers(account, error);
2033 CFArrayForEach(peers, ^(const void *value) {
2034 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
2035 NSString* peerID = (__bridge NSString*) SOSPeerInfoGetPeerID(peer);
2036
2037 //any peerid that is not ours gets added
2038 if(![[account.trust peerID] isEqualToString:peerID])
2039 [peerIDs addObject:peerID];
2040 });
2041 CFReleaseNull(peers);
2042
2043 [keysAndValues enumerateKeysAndObjectsUsingBlock:^(NSString * KVSKey, NSNumber * KVSValue, BOOL *stop) {
2044 __block bool keyMatchesPeerID = false;
2045
2046 //checks for full peer ids
2047 [peerIDs enumerateObjectsUsingBlock:^(id _Nonnull PeerID, NSUInteger idx, BOOL * _Nonnull stop) {
2048 //if key contains peerid of one active peer
2049 if([KVSKey containsString:PeerID]){
2050 secnotice("key-cleanup","key: %@", KVSKey);
2051 keyMatchesPeerID = true;
2052 }
2053 }];
2054 if((([KVSKey hasPrefix:@"ak"] || [KVSKey hasPrefix:@"-ak"]) && !keyMatchesPeerID)
2055 || [KVSKey hasPrefix:@"po"])
2056 [keysToRemove addObject:KVSKey];
2057 }];
2058
2059 secnotice("key-cleanup", "message keys that we should remove! %@", keysToRemove);
2060 secnotice("key-cleanup", "total keys: %lu, cleaning up %lu", (unsigned long)[keysAndValues count], (unsigned long)[keysToRemove count]);
2061
2062 SOSAccountRemoveKVSKeys(account, keysToRemove, processQueue);
2063
2064 //add last cleanup timestamp
2065 SOSAccountWriteLastCleanupTimestampToKVS(account);
2066 return true;
2067
2068 }
2069
2070 bool SOSAccountPopulateKVSWithBadKeys(SOSAccount* account, CFErrorRef* error) {
2071
2072 NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
2073 [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
2074 [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
2075 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
2076 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
2077 [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
2078
2079 dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
2080 dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
2081 dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2082
2083 CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
2084 if (error){
2085 secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
2086 }
2087 dispatch_semaphore_signal(waitSemaphore);
2088 };
2089
2090 SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
2091 dispatch_semaphore_wait(waitSemaphore, finishTime);
2092
2093 return true;
2094 }
2095
2096 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount* account, CFErrorRef* error) {
2097 SOSPeerInfoRef applicant = NULL;
2098 SOSAccountTrustClassic *trust = account.trust;
2099 SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
2100 if(!userKey) return false;
2101 if(![trust ensureFullPeerAvailable:(__bridge CFDictionaryRef)(account.gestalt) deviceID:(__bridge CFStringRef)(account.deviceID) backupKey:(__bridge CFDataRef)(account.backup_key) err:error])
2102 return applicant;
2103 if(!SOSFullPeerInfoPromoteToApplication(trust.fullPeerInfo, userKey, error))
2104 return applicant;
2105 applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault, trust.peerInfo, error);
2106
2107 return applicant;
2108 }
2109
2110
2111 static void
2112 AddStrippedResults(NSMutableArray *results, NSArray *keychainItems, NSMutableSet *seenUUID, bool authoriative)
2113 {
2114 [keychainItems enumerateObjectsUsingBlock:^(NSDictionary* keychainItem, NSUInteger idx, BOOL * _Nonnull stop) {
2115 NSString* parentUUID = keychainItem[(id)kSecAttrPath];
2116 NSString* viewUUID = keychainItem[(id)kSecAttrAccount];
2117 NSString *viewName = [keychainItem objectForKey:(id)kSecAttrServer];
2118
2119 if (parentUUID == NULL || viewUUID == NULL || viewName == NULL)
2120 return;
2121
2122 if([parentUUID isEqualToString:viewUUID] || authoriative){
2123
2124 /* check if we already have this entry */
2125 if ([seenUUID containsObject:viewUUID])
2126 return;
2127
2128 NSData* v_data = [keychainItem objectForKey:(id)kSecValueData];
2129 NSData *key = [[NSData alloc] initWithBase64EncodedData:v_data options:0];
2130
2131 if (key == NULL)
2132 return;
2133
2134 secnotice("piggy", "fetched TLK %@ with name %@", viewName, viewUUID);
2135
2136 NSMutableDictionary* strippedDown = [@{
2137 (id)kSecValueData : key,
2138 (id)kSecAttrServer : viewName,
2139 (id)kSecAttrAccount : viewUUID
2140 } mutableCopy];
2141 if (authoriative)
2142 strippedDown[@"auth"] = @YES;
2143
2144 [results addObject:strippedDown];
2145 [seenUUID addObject:viewUUID];
2146 }
2147 }];
2148 }
2149
2150 static void
2151 AddViewManagerResults(NSMutableArray *results, NSMutableSet *seenUUID)
2152 {
2153 #if OCTAGON
2154 CKKSViewManager* manager = [CKKSViewManager manager];
2155
2156 NSDictionary<NSString *,NSString *> *items = [manager activeTLKs];
2157
2158 for (NSString *view in items) {
2159 NSString *uuid = items[view];
2160 NSDictionary *query = @{
2161 (id)kSecClass : (id)kSecClassInternetPassword,
2162 (id)kSecAttrNoLegacy : @YES,
2163 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2164 (id)kSecAttrAccount : uuid,
2165 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2166 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2167 (id)kSecReturnAttributes: @YES,
2168 (id)kSecReturnData: @YES,
2169 };
2170 CFTypeRef result = NULL;
2171 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2172 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, true);
2173 }
2174 CFReleaseNull(result);
2175 }
2176 #endif
2177 }
2178
2179
2180 NSMutableArray*
2181 SOSAccountGetAllTLKs(void)
2182 {
2183 CFTypeRef result = NULL;
2184 NSMutableArray* results = [NSMutableArray array];
2185 NSMutableSet *seenUUID = [NSMutableSet set];
2186
2187 // first use the TLK from the view manager
2188 AddViewManagerResults(results, seenUUID);
2189
2190 //try to grab tlk-piggy items
2191 NSDictionary* query = @{
2192 (id)kSecClass : (id)kSecClassInternetPassword,
2193 (id)kSecAttrNoLegacy : @YES,
2194 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2195 (id)kSecAttrDescription: @"tlk",
2196 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
2197 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2198 (id)kSecReturnAttributes: @YES,
2199 (id)kSecReturnData: @YES,
2200 };
2201
2202 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2203 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2204 }
2205 CFReleaseNull(result);
2206
2207 //try to grab tlk-piggy items
2208 query = @{
2209 (id)kSecClass : (id)kSecClassInternetPassword,
2210 (id)kSecAttrNoLegacy : @YES,
2211 (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
2212 (id)kSecAttrDescription: @"tlk-piggy",
2213 (id)kSecAttrSynchronizable : (id)kSecAttrSynchronizableAny,
2214 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
2215 (id)kSecReturnAttributes: @YES,
2216 (id)kSecReturnData: @YES,
2217 };
2218
2219 if (SecItemCopyMatching((__bridge CFDictionaryRef)query, &result) == 0) {
2220 AddStrippedResults(results, (__bridge NSArray*)result, seenUUID, false);
2221 }
2222 CFReleaseNull(result);
2223
2224 secnotice("piggy", "Found %d TLKs", (int)[results count]);
2225
2226 return results;
2227 }
2228
2229 static uint8_t* encode_tlk(kTLKTypes type, NSString *name, NSData *keychainData, NSData* uuid,
2230 const uint8_t *der, uint8_t *der_end)
2231 {
2232 if (type != kTLKUnknown) {
2233 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2234 piggy_encode_data(keychainData, der,
2235 piggy_encode_data(uuid, der,
2236 ccder_encode_uint64((uint64_t)type, der, der_end))));
2237 } else {
2238 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2239 piggy_encode_data(keychainData, der,
2240 piggy_encode_data(uuid, der,
2241 der_encode_string((__bridge CFStringRef)name, NULL, der, der_end))));
2242 }
2243 }
2244
2245 static uint8_t* piggy_encode_data(NSData* data,
2246 const uint8_t *der, uint8_t *der_end)
2247 {
2248 return ccder_encode_tl(CCDER_OCTET_STRING, data.length, der,
2249 ccder_encode_body(data.length, data.bytes, der, der_end));
2250
2251 }
2252
2253 static kTLKTypes
2254 name2type(NSString *view)
2255 {
2256 if ([view isEqualToString:@"Manatee"])
2257 return kTLKManatee;
2258 else if ([view isEqualToString:@"Engram"])
2259 return kTLKEngram;
2260 else if ([view isEqualToString:@"AutoUnlock"])
2261 return kTLKAutoUnlock;
2262 if ([view isEqualToString:@"Health"])
2263 return kTLKHealth;
2264 return kTLKUnknown;
2265 }
2266
2267 static unsigned
2268 rank_type(NSString *view)
2269 {
2270 if ([view isEqualToString:@"Manatee"])
2271 return 5;
2272 else if ([view isEqualToString:@"Engram"])
2273 return 4;
2274 else if ([view isEqualToString:@"AutoUnlock"])
2275 return 3;
2276 if ([view isEqualToString:@"Health"])
2277 return 2;
2278 return 0;
2279 }
2280
2281 static NSData *
2282 parse_uuid(NSString *uuidString)
2283 {
2284 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
2285 uuid_t uuidblob;
2286 [uuid getUUIDBytes:uuidblob];
2287 return [NSData dataWithBytes:uuidblob length:sizeof(uuid_t)];
2288 }
2289 static size_t
2290 piggy_sizeof_data(NSData* data)
2291 {
2292 return ccder_sizeof(CCDER_OCTET_STRING, [data length]);
2293 }
2294
2295 static size_t sizeof_keychainitem(kTLKTypes type, NSString *name, NSData* keychainData, NSData* uuid) {
2296 if (type != kTLKUnknown) {
2297 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2298 piggy_sizeof_data(keychainData) +
2299 piggy_sizeof_data(uuid) +
2300 ccder_sizeof_uint64(type));
2301 } else {
2302 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2303 piggy_sizeof_data(keychainData) +
2304 piggy_sizeof_data(uuid) +
2305 der_sizeof_string((__bridge CFStringRef)name, NULL));
2306 }
2307 }
2308
2309 NSArray<NSDictionary*>*
2310 SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks)
2311 {
2312 NSMutableArray<NSDictionary*>* sortedTLKs = [tlks mutableCopy];
2313
2314 [sortedTLKs sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
2315 unsigned rank1 = rank_type(obj1[(__bridge id)kSecAttrServer]);
2316 if (obj1[@"auth"] != NULL)
2317 rank1 += 1000;
2318 unsigned rank2 = rank_type(obj2[(__bridge id)kSecAttrServer]);
2319 if (obj2[@"auth"] != NULL)
2320 rank2 += 1000;
2321
2322 /*
2323 * Sort by rank (higher better), but prefer TLK that are authoriative (ie used by CKKSViewManager),
2324 * since we are sorting backward, the Ascending/Descending looks wrong below.
2325 */
2326 if (rank1 > rank2) {
2327 return NSOrderedAscending;
2328 } else if (rank1 < rank2) {
2329 return NSOrderedDescending;
2330 }
2331 return NSOrderedSame;
2332 }];
2333
2334 return sortedTLKs;
2335 }
2336
2337 static NSArray<NSData *> *
2338 build_tlks(NSArray<NSDictionary*>* tlks)
2339 {
2340 NSMutableArray *array = [NSMutableArray array];
2341 NSArray<NSDictionary*>* sortedTLKs = SOSAccountSortTLKS(tlks);
2342
2343 for (NSDictionary *item in sortedTLKs) {
2344 NSData* keychainData = item[(__bridge id)kSecValueData];
2345 NSString* name = item[(__bridge id)kSecAttrServer];
2346 NSString *uuidString = item[(__bridge id)kSecAttrAccount];
2347 NSData* uuid = parse_uuid(uuidString);
2348
2349 NSMutableData *tlk = [NSMutableData dataWithLength:sizeof_keychainitem(name2type(name), name, keychainData, uuid)];
2350
2351 unsigned char *der = [tlk mutableBytes];
2352 unsigned char *der_end = der + [tlk length];
2353
2354 if (encode_tlk(name2type(name), name, keychainData, uuid, der, der_end) == NULL)
2355 return NULL;
2356
2357 secnotice("piggy", "preparing TLK in order: %@: %@", name, uuidString);
2358
2359 [array addObject:tlk];
2360 }
2361 return array;
2362 }
2363
2364 static NSArray<NSData *> *
2365 build_identities(NSArray<NSData *>* identities)
2366 {
2367 NSMutableArray *array = [NSMutableArray array];
2368 for (NSData *item in identities) {
2369 NSMutableData *ident = [NSMutableData dataWithLength:ccder_sizeof_raw_octet_string([item length])];
2370
2371 unsigned char *der = [ident mutableBytes];
2372 unsigned char *der_end = der + [ident length];
2373
2374 ccder_encode_raw_octet_string([item length], [item bytes], der, der_end);
2375 [array addObject:ident];
2376 }
2377 return array;
2378 }
2379
2380
2381
2382 static unsigned char *
2383 encode_data_array(NSArray<NSData*>* data, unsigned char *der, unsigned char *der_end)
2384 {
2385 unsigned char *body_end = der_end;
2386 for (NSData *datum in data) {
2387 der_end = ccder_encode_body([datum length], [datum bytes], der, der_end);
2388 if (der_end == NULL)
2389 return NULL;
2390 }
2391 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, body_end, der, der_end);
2392 }
2393
2394 static size_t sizeof_piggy(size_t identities_size, size_t tlk_size)
2395 {
2396 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
2397 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, identities_size) +
2398 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, tlk_size));
2399 }
2400
2401 static NSData *encode_piggy(size_t IdentitiesBudget,
2402 size_t TLKBudget,
2403 NSArray<NSData*>* identities,
2404 NSArray<NSDictionary*>* tlks)
2405 {
2406 NSArray<NSData *> *encodedTLKs = build_tlks(tlks);
2407 NSArray<NSData *> *encodedIdentities = build_identities(identities);
2408 NSMutableArray<NSData *> *budgetArray = [NSMutableArray array];
2409 NSMutableArray<NSData *> *identitiesArray = [NSMutableArray array];
2410 size_t payloadSize = 0, identitiesSize = 0;
2411 NSMutableData *result = NULL;
2412
2413 for (NSData *tlk in encodedTLKs) {
2414 if (TLKBudget - payloadSize < [tlk length])
2415 break;
2416 [budgetArray addObject:tlk];
2417 payloadSize += tlk.length;
2418 }
2419 secnotice("piggy", "sending %d tlks", (int)budgetArray.count);
2420
2421 for (NSData *ident in encodedIdentities) {
2422 if (IdentitiesBudget - identitiesSize < [ident length])
2423 break;
2424 [identitiesArray addObject:ident];
2425 identitiesSize += ident.length;
2426 }
2427 secnotice("piggy", "sending %d identities", (int)identitiesArray.count);
2428
2429
2430 size_t piggySize = sizeof_piggy(identitiesSize, payloadSize);
2431
2432 result = [NSMutableData dataWithLength:piggySize];
2433
2434 unsigned char *der = [result mutableBytes];
2435 unsigned char *der_end = der + [result length];
2436
2437 if (ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
2438 encode_data_array(identitiesArray, der,
2439 encode_data_array(budgetArray, der, der_end))) != [result mutableBytes])
2440 return NULL;
2441
2442 return result;
2443 }
2444
2445 static const size_t SOSCCIdentitiesBudget = 120;
2446 static const size_t SOSCCTLKBudget = 500;
2447
2448 NSData *
2449 SOSPiggyCreateInitialSyncData(NSArray<NSData*>* identities, NSArray<NSDictionary *>* tlks)
2450 {
2451 return encode_piggy(SOSCCIdentitiesBudget, SOSCCTLKBudget, identities, tlks);
2452 }
2453
2454 CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountCopyiCloudIdentities(SOSAccount* account)
2455 {
2456 CFMutableArrayRef identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
2457
2458 SOSCircleForEachActivePeer(account.trust.trustedCircle, ^(SOSPeerInfoRef peer) {
2459 if(SOSPeerInfoIsCloudIdentity(peer)) {
2460 CFArrayAppendValue(identities, peer);
2461 }
2462 });
2463 return identities;
2464 }
2465
2466 CFDataRef SOSAccountCopyInitialSyncData(SOSAccount* account, CFErrorRef *error) {
2467 CFMutableArrayRef identities = SOSAccountCopyiCloudIdentities(account);
2468 secnotice("piggy", "identities: %@", identities);
2469
2470 NSMutableArray *encodedIdenities = [NSMutableArray array];
2471 CFIndex i, count = CFArrayGetCount(identities);
2472 for (i = 0; i < count; i++) {
2473 SOSPeerInfoRef fpi = (SOSPeerInfoRef)CFArrayGetValueAtIndex(identities, i);
2474 NSData *data = CFBridgingRelease(SOSPeerInfoCopyData(fpi, error));
2475 if (data)
2476 [encodedIdenities addObject:data];
2477 }
2478 CFRelease(identities);
2479
2480 NSMutableArray* tlks = SOSAccountGetAllTLKs();
2481
2482 return CFBridgingRetain(SOSPiggyCreateInitialSyncData(encodedIdenities, tlks));
2483 }
2484
2485 static void pbNotice(CFStringRef operation, SOSAccount* account, SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, PiggyBackProtocolVersion version) {
2486 CFStringRef pkeyID = SOSCopyIDOfKey(pubKey, NULL);
2487 if(pkeyID == NULL) pkeyID = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("Unknown"));
2488 CFStringRef sigID = SOSCopyIDOfDataBuffer(signature, NULL);
2489 if(sigID == NULL) sigID = CFStringCreateCopy(kCFAllocatorDefault, CFSTR("No Signature"));
2490 CFStringRef accountName = SOSAccountGetValue(account, kSOSAccountName, NULL);
2491 if(accountName == NULL) {
2492 accountName = CFSTR("Unavailable");
2493 }
2494 CFStringRef circleHash = SOSCircleCopyHashString(account.trust.trustedCircle);
2495
2496 secnotice("circleOps",
2497 "%@: Joining blob for account: %@ for piggyback (V%d) gencount: %@ pubkey: %@ signatureID: %@ starting circle hash: %@",
2498 operation, accountName, version, gencount, pkeyID, sigID, circleHash);
2499 CFReleaseNull(pkeyID);
2500 CFReleaseNull(sigID);
2501 CFReleaseNull(circleHash);
2502 }
2503
2504 CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccount* account, SOSPeerInfoRef applicant, CFErrorRef *error) {
2505 SOSGenCountRef gencount = NULL;
2506 CFDataRef signature = NULL;
2507 SecKeyRef ourKey = NULL;
2508
2509 CFDataRef pbblob = NULL;
2510 SOSCircleRef prunedCircle = NULL;
2511
2512 secnotice("circleOps", "Making circle joining piggyback blob as sponsor (SOSAccountCopyCircleJoiningBlob)");
2513
2514 SOSCCStatus circleStat = [account getCircleStatus:error];
2515 if(circleStat != kSOSCCInCircle) {
2516 secnotice("circleOps", "Invalid circle status: %@ to accept piggyback as sponsor (SOSAccountCopyCircleJoiningBlob)", SOSCCGetStatusDescription(circleStat));
2517 return NULL;
2518 }
2519
2520 SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
2521 require_quiet(userKey, errOut);
2522
2523 require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
2524 require_action_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut,
2525 secnotice("circleOps", "Peer application wasn't signed with the correct userKey"));
2526
2527 {
2528 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
2529 ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
2530 require_quiet(ourKey, errOut);
2531 }
2532
2533 SOSCircleRef currentCircle = [account.trust getCircle:error];
2534 require_quiet(currentCircle, errOut);
2535
2536 prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
2537 require_quiet(prunedCircle, errOut);
2538 require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
2539
2540 gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
2541
2542 signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
2543 require_quiet(signature, errOut);
2544 pbNotice(CFSTR("Accepting"), account, gencount, ourKey, signature, kPiggyV1);
2545 pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, error);
2546
2547 errOut:
2548 CFReleaseNull(prunedCircle);
2549 CFReleaseNull(gencount);
2550 CFReleaseNull(signature);
2551 CFReleaseNull(ourKey);
2552
2553 if(!pbblob && error != NULL) {
2554 secnotice("circleOps", "Failed to make circle joining piggyback blob as sponsor %@", *error);
2555 }
2556
2557 return pbblob;
2558 }
2559
2560 bool SOSAccountJoinWithCircleJoiningBlob(SOSAccount* account, CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error) {
2561 bool retval = false;
2562 SecKeyRef userKey = NULL;
2563 SOSAccountTrustClassic *trust = account.trust;
2564 SOSGenCountRef gencount = NULL;
2565 CFDataRef signature = NULL;
2566 SecKeyRef pubKey = NULL;
2567 bool setInitialSyncTimeoutToV0 = false;
2568
2569 secnotice("circleOps", "Joining circles through piggyback (SOSAccountCopyCircleJoiningBlob)");
2570
2571 if (!isData(joiningBlob)) {
2572 secnotice("circleOps", "Bad data blob: piggyback (SOSAccountCopyCircleJoiningBlob)");
2573 return false;
2574 }
2575
2576 userKey = SOSAccountGetPrivateCredential(account, error);
2577 if(!userKey) {
2578 secnotice("circleOps", "Failed - no private credential %@: piggyback (SOSAccountCopyCircleJoiningBlob)", *error);
2579 return retval;
2580 }
2581
2582 if (!SOSPiggyBackBlobCreateFromData(&gencount, &pubKey, &signature, joiningBlob, version, &setInitialSyncTimeoutToV0, error)) {
2583 secnotice("circleOps", "Failed - decoding blob %@: piggyback (SOSAccountCopyCircleJoiningBlob)", *error);
2584 return retval;
2585 }
2586
2587 if(setInitialSyncTimeoutToV0){
2588 secnotice("circleOps", "setting flag in account for piggyback v0");
2589 SOSAccountSetValue(account, kSOSInitialSyncTimeoutV0, kCFBooleanTrue, NULL);
2590 } else {
2591 secnotice("circleOps", "clearing flag in account for piggyback v0");
2592 SOSAccountClearValue(account, kSOSInitialSyncTimeoutV0, NULL);
2593 }
2594 SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
2595
2596 pbNotice(CFSTR("Joining"), account, gencount, pubKey, signature, version);
2597
2598 retval = [trust modifyCircle:account.circle_transport err:error action:^bool(SOSCircleRef copyOfCurrent) {
2599 return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
2600 gencount,
2601 pubKey,
2602 signature,
2603 trust.fullPeerInfo, error);;
2604 }];
2605
2606 CFReleaseNull(gencount);
2607 CFReleaseNull(pubKey);
2608 CFReleaseNull(signature);
2609
2610 return retval;
2611 }
2612
2613 static char boolToChars(bool val, char truechar, char falsechar) {
2614 return val? truechar: falsechar;
2615 }
2616
2617 #define ACCOUNTLOGSTATE "accountLogState"
2618 void SOSAccountLogState(SOSAccount* account) {
2619 bool hasPubKey = account.accountKey != NULL;
2620 SOSAccountTrustClassic *trust = account.trust;
2621 bool pubTrusted = account.accountKeyIsTrusted;
2622 bool hasPriv = account.accountPrivateKey != NULL;
2623 SOSCCStatus stat = [account getCircleStatus:NULL];
2624
2625 CFStringRef userPubKeyID = (account.accountKey) ? SOSCopyIDOfKeyWithLength(account.accountKey, 8, NULL):
2626 CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
2627
2628 secnotice(ACCOUNTLOGSTATE, "Start");
2629
2630 secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
2631 boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
2632 userPubKeyID,
2633 SOSAccountGetSOSCCStatusString(stat)
2634 );
2635 CFReleaseNull(userPubKeyID);
2636 if(trust.trustedCircle) SOSCircleLogState(ACCOUNTLOGSTATE, trust.trustedCircle, account.accountKey, (__bridge CFStringRef)(account.peerID));
2637 else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
2638 }
2639
2640 void SOSAccountLogViewState(SOSAccount* account) {
2641 bool isInCircle = [account.trust isInCircleOnly:NULL];
2642 require_quiet(isInCircle, imOut);
2643 SOSPeerInfoRef mpi = account.peerInfo;
2644 bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
2645 bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
2646
2647 CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
2648 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2649 secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
2650 boolToChars(isInitialComplete, 'I', 'i'),
2651 boolToChars(isBackupComplete, 'B', 'b'),
2652 description);
2653 });
2654 CFReleaseNull(views);
2655 CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
2656 CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
2657 secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
2658 });
2659 CFReleaseNull(unsyncedViews);
2660
2661 imOut:
2662 secnotice(ACCOUNTLOGSTATE, "Finish");
2663
2664 return;
2665 }
2666
2667
2668 void SOSAccountSetTestSerialNumber(SOSAccount* account, CFStringRef serial) {
2669 if(!isString(serial)) return;
2670 CFMutableDictionaryRef newv2dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
2671 CFDictionarySetValue(newv2dict, sSerialNumberKey, serial);
2672 [account.trust updateV2Dictionary:account v2:newv2dict];
2673 }
2674
2675 void SOSAccountResetOTRNegotiationCoder(SOSAccount* account, CFStringRef peerid)
2676 {
2677 secnotice("otrtimer", "timer fired!");
2678 CFErrorRef error = NULL;
2679 SecADAddValueForScalarKey((__bridge CFStringRef) SecSOSAggdReattemptOTRNegotiation,1);
2680
2681 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2682 SOSEngineWithPeerID(engine, peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2683 if(SOSCoderIsCoderInAwaitingState(coder)){
2684 secnotice("otrtimer", "coder is in awaiting state, restarting coder");
2685 CFErrorRef localError = NULL;
2686 SOSCoderReset(coder);
2687 if(SOSCoderStart(coder, &localError) == kSOSCoderFailure){
2688 secerror("Attempt to recover coder failed to restart: %@", localError);
2689 }
2690 else{
2691 secnotice("otrtimer", "coder restarted!");
2692 SOSEngineSetCodersNeedSaving(engine, true);
2693 SOSPeerSetMustSendMessage(peer, true);
2694 SOSCCRequestSyncWithPeer(SOSPeerGetID(peer));
2695 }
2696 SOSPeerOTRTimerIncreaseOTRNegotiationRetryCount(account, (__bridge NSString*)SOSPeerGetID(peer));
2697 SOSPeerRemoveOTRTimerEntry(peer);
2698 SOSPeerOTRTimerRemoveRTTTimeoutForPeer(account, (__bridge NSString*)SOSPeerGetID(peer));
2699 SOSPeerOTRTimerRemoveLastSentMessageTimestamp(account, (__bridge NSString*)SOSPeerGetID(peer));
2700 }
2701 else{
2702 secnotice("otrtimer", "time fired but out of negotiation! Not restarting coder");
2703 }
2704 });
2705 if(error)
2706 {
2707 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2708 }
2709 CFReleaseNull(error);
2710 }
2711
2712 void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* peerid, NSString* accessGroup)
2713 {
2714 __block SOSAccount* account = txn.account;
2715 CFErrorRef error = NULL;
2716
2717 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(txn.account.factory, SOSCircleGetName(account.trust.trustedCircle), NULL);
2718 SOSEngineWithPeerID(engine, (__bridge CFStringRef)peerid, &error, ^(SOSPeerRef peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
2719
2720 NSString *peer_id = (__bridge NSString*)SOSPeerGetID(peer);
2721 PeerRateLimiter *limiter = (__bridge PeerRateLimiter*)SOSPeerGetRateLimiter(peer);
2722 CFErrorRef error = NULL;
2723 NSData* message = [limiter.accessGroupToNextMessageToSend objectForKey:accessGroup];
2724
2725 if(message){
2726 secnotice("ratelimit","SOSPeerRateLimiter timer went off! sending:%@ \n to peer:%@", message, peer_id);
2727 bool sendResult = [account.kvs_message_transport SOSTransportMessageSendMessage:account.kvs_message_transport id:(__bridge CFStringRef)peer_id messageToSend:(__bridge CFDataRef)message err:&error];
2728
2729 if(!sendResult || error){
2730 secnotice("ratelimit", "could not send message: %@", error);
2731 }
2732 }
2733 [limiter.accessGroupRateLimitState setObject:[[NSNumber alloc]initWithLong:RateLimitStateCanSend] forKey:accessGroup];
2734 [limiter.accessGroupToTimer removeObjectForKey:accessGroup];
2735 [limiter.accessGroupToNextMessageToSend removeObjectForKey:accessGroup];
2736 });
2737
2738 if(error)
2739 {
2740 secnotice("otrtimer","error grabbing engine for peer id: %@, error:%@", peerid, error);
2741 }
2742 CFReleaseNull(error);
2743 }
2744
2745 @end
2746
2747