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