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