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