]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTClique.m
Security-59306.41.2.tar.gz
[apple/security.git] / keychain / ot / OTClique.m
1 /*
2 * Copyright (c) 2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if __OBJC2__
25
26 #import <TargetConditionals.h>
27 #import <Foundation/Foundation.h>
28
29 #import "keychain/ot/OTClique.h"
30 #import "keychain/ot/OTConstants.h"
31 #import "keychain/ot/OTDefines.h"
32
33 #import <utilities/SecCFWrappers.h>
34 #import <utilities/debugging.h>
35
36 #import "keychain/SecureObjectSync/SOSCloudCircle.h"
37 #import "KeychainCircle/PairingChannel.h"
38 #import <Security/SecBase.h>
39
40 const NSString* kSecEntitlementPrivateOctagonEscrow = @"com.apple.private.octagon.escrow-content";
41
42 #if OCTAGON
43 #import <AuthKit/AuthKit.h>
44 #import <AuthKit/AuthKit_Private.h>
45 #import <SoftLinking/SoftLinking.h>
46 #import <CloudServices/SecureBackup.h>
47 #import <CloudServices/SecureBackupConstants.h>
48 #import "keychain/ot/OTControl.h"
49 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
50
51 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
52 SOFT_LINK_FRAMEWORK(PrivateFrameworks, CloudServices);
53
54 #pragma clang diagnostic push
55 #pragma clang diagnostic ignored "-Wstrict-prototypes"
56 SOFT_LINK_CLASS(KeychainCircle, KCPairingChannel);
57 SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
58 SOFT_LINK_CLASS(CloudServices, SecureBackup);
59 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
60
61 #pragma clang diagnostic pop
62 #endif
63
64 OTCliqueCDPContextType OTCliqueCDPContextTypeNone = @"cdpContextTypeNone";
65 OTCliqueCDPContextType OTCliqueCDPContextTypeSignIn = @"cdpContextTypeSignIn";
66 OTCliqueCDPContextType OTCliqueCDPContextTypeRepair = @"cdpContextTypeRepair";
67 OTCliqueCDPContextType OTCliqueCDPContextTypeFinishPasscodeChange = @"cdpContextTypeFinishPasscodeChange";
68 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyGenerate = @"cdpContextTypeRecoveryKeyGenerate";
69 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyNew = @"cdpContextTypeRecoveryKeyNew";
70 OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode = @"cdpContextTypeUpdatePasscode";
71
72 NSString* OTCliqueStatusToString(CliqueStatus status)
73 {
74 switch(status) {
75 case CliqueStatusIn:
76 return @"CliqueStatusIn";
77 case CliqueStatusNotIn:
78 return @"CliqueStatusNotIn";
79 case CliqueStatusPending:
80 return @"CliqueStatusPending";
81 case CliqueStatusAbsent:
82 return @"CliqueStatusAbsent";
83 case CliqueStatusNoCloudKitAccount:
84 return @"CliqueStatusNoCloudKitAccount";
85 case CliqueStatusError:
86 return @"CliqueStatusError";
87 };
88 }
89 CliqueStatus OTCliqueStatusFromString(NSString* str)
90 {
91 if([str isEqualToString: @"CliqueStatusIn"]) {
92 return CliqueStatusIn;
93 } else if([str isEqualToString: @"CliqueStatusNotIn"]) {
94 return CliqueStatusNotIn;
95 } else if([str isEqualToString: @"CliqueStatusPending"]) {
96 return CliqueStatusPending;
97 } else if([str isEqualToString: @"CliqueStatusAbsent"]) {
98 return CliqueStatusAbsent;
99 } else if([str isEqualToString: @"CliqueStatusNoCloudKitAccount"]) {
100 return CliqueStatusNoCloudKitAccount;
101 } else if([str isEqualToString: @"CliqueStatusError"]) {
102 return CliqueStatusError;
103 }
104
105 return CliqueStatusError;
106 }
107
108
109 @implementation OTConfigurationContext
110 - (OTControl* _Nullable)makeOTControl:(NSError**)error
111 {
112 #if OCTAGON
113 if (self.otControl) {
114 return self.otControl;
115 }
116 return [OTControl controlObject:true error:error];
117 #else
118 return nil;
119 #endif
120 }
121 @end
122
123 @implementation OTBottleIDs
124 @end
125
126 @implementation OTOperationConfiguration
127
128 - (instancetype)init {
129 if ((self = [super init]) == nil) {
130 return nil;
131 }
132 _timeoutWaitForCKAccount = 10 * NSEC_PER_SEC;
133 _qualityOfService = NSQualityOfServiceDefault;
134 _discretionaryNetwork = NO;
135 _useCachedAccountStatus = NO;
136 return self;
137 }
138
139 + (BOOL)supportsSecureCoding {
140 return YES;
141 }
142
143 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
144 [coder encodeObject:@(_timeoutWaitForCKAccount) forKey:@"timeoutWaitForCKAccount"];
145 [coder encodeObject:@(_qualityOfService) forKey:@"qualityOfService"];
146 [coder encodeObject:@(_discretionaryNetwork) forKey:@"discretionaryNetwork"];
147 [coder encodeObject:@(_useCachedAccountStatus) forKey:@"useCachedAccountStatus"];
148 }
149
150 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
151 _timeoutWaitForCKAccount = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"timeoutWaitForCKAccount"] unsignedLongLongValue];
152 _qualityOfService = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"qualityOfService"] integerValue];
153 _discretionaryNetwork = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"discretionaryNetwork"] boolValue];
154 _useCachedAccountStatus = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"useCachedAccountStatus"] boolValue];
155 return self;
156 }
157
158 @end
159
160
161 @interface OTClique ()
162 @property (nonatomic, copy) NSString* cliqueMemberIdentifier;
163 @property (nonatomic, strong) OTConfigurationContext *ctx;
164 @property (nonatomic, strong) NSMutableDictionary *defaults;
165 @end
166
167 @implementation OTClique
168
169 + (BOOL)platformSupportsSOS
170 {
171 return (OctagonPlatformSupportsSOS() && OctagonIsSOSFeatureEnabled());
172 }
173
174 // defaults write com.apple.security.octagon enable -bool YES
175 -(BOOL)isOctagonPairingEnabled {
176 BOOL nsDefaults = self.defaults[OTDefaultsOctagonEnable] ? [self.defaults[OTDefaultsOctagonEnable] boolValue] : OctagonIsEnabled();
177 secnotice("octagon", "pairing is %@", nsDefaults ? @"on" : @"off");
178 return nsDefaults;
179 }
180
181 - (void)setPairingDefault:(BOOL)defaults
182 {
183 self.defaults[OTDefaultsOctagonEnable] = @(defaults);
184 }
185
186 - (void)removePairingDefault
187 {
188 [self.defaults removeObjectForKey:OTDefaultsOctagonEnable];
189 }
190
191 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
192 {
193 #if OCTAGON
194 self = [super init];
195 if(self){
196 _ctx = [[OTConfigurationContext alloc]init];
197 _ctx.context = ctx.context ?: OTDefaultContext;
198 _ctx.dsid = [ctx.dsid copy];
199 _ctx.altDSID = [ctx.altDSID copy];
200 _ctx.analytics = ctx.analytics;
201 _ctx.otControl = ctx.otControl;
202
203 self.defaults = [NSMutableDictionary dictionary];
204 }
205 return self;
206 #else
207 NSAssert(false, @"OTClique is not implemented on this platform");
208 return nil;
209 #endif // OCTAGON
210 }
211
212 - (NSString* _Nullable)cliqueMemberIdentifier
213 {
214 #if OCTAGON
215 __block NSString* retPeerID = nil;
216
217 if(OctagonIsEnabled()) {
218 NSError* localError = nil;
219 OTControl* control = [self makeOTControl:&localError];
220 if(!control) {
221 secerror("octagon: Failed to create OTControl: %@", localError);
222 return nil;
223 }
224
225 [control fetchEgoPeerID:nil
226 context:self.ctx.context
227 reply:^(NSString* peerID, NSError* error) {
228 if(error) {
229 secerror("octagon: Failed to fetch octagon peer ID: %@", error);
230 }
231 retPeerID = peerID;
232 }];
233 secnotice("clique", "cliqueMemberIdentifier(octagon) received %@", retPeerID);
234 }
235
236 if([OTClique platformSupportsSOS]) {
237 CFErrorRef error = NULL;
238 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
239 retPeerID = (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
240 CFReleaseNull(me);
241 }
242
243 secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
244 return retPeerID;
245 #else
246 return nil;
247 #endif
248 }
249
250 #if OCTAGON
251 - (OTControl* _Nullable)makeOTControl:(NSError**)error
252 {
253 return [self.ctx makeOTControl:error];
254 }
255
256 - (BOOL)establish:(NSError**)error
257 {
258 secnotice("clique-establish", "establish started");
259
260 OTControl* control = [self makeOTControl:error];
261 if(!control) {
262 return false;
263 }
264
265 __block BOOL success = NO;
266 __block NSError* localError = nil;
267 //only establish
268 [control establish:nil context:self.ctx.context altDSID:self.ctx.altDSID reply:^(NSError * _Nullable operationError) {
269
270 if(operationError) {
271 secnotice("clique-establish", "reenact establish returned an error: %@", operationError);
272 }
273 success = !!operationError;
274 localError = operationError;
275 }];
276
277 if(localError && error) {
278 *error = localError;
279 }
280 secnotice("clique-establish", "establish complete: %@", success ? @"YES" : @"NO");
281
282 return success;
283 }
284
285 - (BOOL)resetAndEstablish:(CuttlefishResetReason)resetReason error:(NSError**)error
286 {
287 secnotice("clique-resetandestablish", "resetAndEstablish started");
288
289 OTControl* control = [self makeOTControl:error];
290
291 if(!control) {
292 return false;
293 }
294
295 __block BOOL success = NO;
296 __block NSError* localError = nil;
297 [control resetAndEstablish:nil context:self.ctx.context altDSID:self.ctx.altDSID resetReason:resetReason reply:^(NSError * _Nullable operationError) {
298
299 if(operationError) {
300 secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
301 }
302 success = !!operationError;
303 localError = operationError;
304 }];
305
306 if(localError && error) {
307 *error = localError;
308 }
309
310 secnotice("clique-resetandestablish", "establish complete: %@", success ? @"YES" : @"NO");
311
312 return success;
313 }
314 #endif // OCTAGON
315
316 + (OTClique*)newFriendsWithContextData:(OTConfigurationContext*)data error:(NSError * __autoreleasing *)error
317 {
318 return [OTClique newFriendsWithContextData:data resetReason:CuttlefishResetReasonUserInitiatedReset error:error];
319 }
320
321 + (OTClique*)newFriendsWithContextData:(OTConfigurationContext*)data resetReason:(CuttlefishResetReason)resetReason error:(NSError * __autoreleasing *)error
322 {
323 #if OCTAGON
324 secnotice("clique-newfriends", "makeNewFriends invoked using context: %@, dsid: %@", data.context, data.dsid);
325 bool result = false;
326
327 OTClique* clique = [[OTClique alloc] initWithContextData:data error:error];
328
329 if(OctagonIsEnabled()) {
330 NSError* localError = nil;
331 [clique resetAndEstablish:resetReason error:&localError];
332
333 if(localError) {
334 secnotice("clique-newfriends", "account reset failed: %@", localError);
335 if(error) {
336 *error = localError;
337 }
338 return nil;
339 } else {
340 secnotice("clique-newfriends", "Octagon account reset succeeded");
341 }
342 }
343
344 if([OTClique platformSupportsSOS]) {
345 CFErrorRef resetError = NULL;
346 NSData* analyticsData = nil;
347 if(data.analytics) {
348 NSError* encodingError = nil;
349 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:data.analytics requiringSecureCoding:YES error:&encodingError];
350
351 if(encodingError) {
352 secnotice("clique-newfriends", "newFriendsWithContextData: unable to serialize analytics: %@", encodingError);
353 }
354 }
355
356 if(analyticsData) {
357 result = SOSCCResetToEmptyWithAnalytics((__bridge CFDataRef)analyticsData, &resetError);
358 } else {
359 result = SOSCCResetToEmpty(&resetError);
360 }
361
362 if(!result || resetError){
363 secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
364 if(error) {
365 *error = CFBridgingRelease(resetError);
366 }
367 return nil;
368 }
369 secnotice("clique-newfriends", "newFriendsWithContextData: reset the SOS circle");
370 } else {
371 secnotice("clique-newfriends", "newFriendsWithContextData: SOS disabled on this platform");
372 }
373 secnotice("clique-newfriends", "makeNewFriends complete");
374
375 return clique;
376
377 #else // !OCTAGON
378 if (error)
379 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
380 return NULL;
381 #endif
382 }
383
384 + (OTClique* _Nullable)performEscrowRecoveryWithContextData:(OTConfigurationContext*)data
385 escrowArguments:(NSDictionary*)sbdRecoveryArguments
386 error:(NSError**)error
387 {
388 #if OCTAGON
389 secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
390
391 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
392
393 NSDictionary* recoveredInformation = nil;
394 NSError* recoverError = [sb recoverWithInfo:sbdRecoveryArguments results:&recoveredInformation];
395
396 if(recoverError) {
397 secnotice("clique-recovery", "sbd escrow recovery failed: %@", recoverError);
398 if([OTClique platformSupportsSOS]) {
399 if(recoverError.code == 17 /* kSecureBackupRestoringLegacyBackupKeychainError */ && [recoverError.domain isEqualToString:getkSecureBackupErrorDomain()]) { /* XXX */
400 secnotice("clique-recovery", "Can't restore legacy backup with no keybag. Resetting SOS to offering");
401 CFErrorRef blowItAwayError = NULL;
402 bool successfulReset = SOSCCResetToOffering(&blowItAwayError);
403 if(!successfulReset || blowItAwayError) {
404 secerror("clique-recovery: failed to reset to offering:%@", blowItAwayError);
405 } else {
406 secnotice("clique-recovery", "resetting SOS circle successful");
407 }
408 } else {
409 if(error) {
410 *error = recoverError;
411 }
412 return nil;
413 }
414 } else {
415 if(error){
416 *error = recoverError;
417 }
418 return nil;
419 }
420 }
421
422 NSError* localError = nil;
423 OTClique* clique = [[OTClique alloc] initWithContextData:data
424 error:&localError];
425
426 if(!clique || localError) {
427 secnotice("clique-recovery", "unable to create otclique: %@", localError);
428 if(error) {
429 *error = localError;
430 }
431 return nil;
432 }
433
434 OTControl* control = [clique makeOTControl:&localError];
435 if (!control) {
436 secnotice("clique-recovery", "unable to create otcontrol: %@", localError);
437 if (error) {
438 *error = localError;
439 }
440 return nil;
441 }
442
443 NSString *bottleID = recoveredInformation[@"bottleID"];
444 NSString *isValid = recoveredInformation[@"bottleValid"];
445 NSData *bottledPeerEntropy = recoveredInformation[@"EscrowServiceEscrowData"][@"BottledPeerEntropy"];
446 bool shouldResetOctagon = false;
447
448 if(bottledPeerEntropy && bottleID && [isValid isEqualToString:@"valid"]){
449 secnotice("clique-recovery", "recovering from bottle: %@", bottleID);
450 __block NSError* restoreBottleError = nil;
451
452 //restore bottle!
453 [control restore:OTCKContainerName
454 contextID:data.context
455 bottleSalt:data.altDSID
456 entropy:bottledPeerEntropy
457 bottleID:bottleID
458 reply:^(NSError * _Nullable restoreError) {
459 if(restoreError) {
460 secnotice("clique-recovery", "restore bottle errored: %@", restoreError);
461 } else {
462 secnotice("clique-recovery", "restoring bottle succeeded");
463 }
464 restoreBottleError = restoreError;
465 }];
466
467 if(restoreBottleError) {
468 if(error){
469 *error = restoreBottleError;
470 }
471 return nil;
472 }
473 } else {
474 shouldResetOctagon = true;
475 }
476
477 if(OctagonPlatformSupportsSOS()) {
478 secnotice("clique-recovery", "attempting joinAfterRestore");
479 [clique joinAfterRestore:&localError];
480 secnotice("clique-recovery", "joinAfterRestore: %@", localError);
481 }
482
483 if(shouldResetOctagon) {
484 secnotice("clique-recovery", "bottle %@ is not valid, resetting octagon", bottleID);
485 NSError* resetError = nil;
486 [clique resetAndEstablish:CuttlefishResetReasonNoBottleDuringEscrowRecovery error:&resetError];
487 if(resetError) {
488 secnotice("clique-recovery", "failed to reset octagon: %@", resetError);
489 } else{
490 secnotice("clique-recovery", "reset octagon succeeded");
491 }
492 }
493
494 secnotice("clique-recovery", "recovery complete: %@", clique);
495
496 return clique;
497 #else
498 if (error) {
499 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
500 }
501 return NULL;
502 #endif
503 }
504
505
506 - (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx
507 {
508 #if OCTAGON
509 return [getKCPairingChannelClass() pairingChannelInitiator:ctx];
510 #else
511 return NULL;
512 #endif
513 }
514
515 - (KCPairingChannel * _Nullable)setupPairingChannelAsInitator:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
516 {
517 if (error) {
518 *error = nil;
519 }
520 return [self setupPairingChannelAsInitiator:ctx];
521 }
522
523 - (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx
524 {
525 #if OCTAGON
526 return [getKCPairingChannelClass() pairingChannelAcceptor:ctx];
527 #else
528 return NULL;
529 #endif
530 }
531
532 - (KCPairingChannel * _Nullable)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
533 {
534 if (error) {
535 *error = nil;
536 }
537
538 return [self setupPairingChannelAsAcceptor:ctx];
539 }
540
541
542 - (CliqueStatus)_fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing *)error
543 {
544 #if OCTAGON
545 __block CliqueStatus sosStatus = CliqueStatusError;
546 __block CliqueStatus octagonStatus = CliqueStatusError;
547
548 // Octagon is supreme.
549
550 if(OctagonIsEnabled()) {
551 OTControl* control = [self makeOTControl:error];
552 if(!control) {
553 secnotice("clique-status", "cliqueStatus noOTControl");
554 return CliqueStatusError;
555 }
556
557 __block NSError* localError = nil;
558 [control fetchCliqueStatus:nil context:self.ctx.context configuration:configuration reply:^(CliqueStatus cliqueStatus, NSError * _Nullable fetchError) {
559 if(fetchError){
560 octagonStatus = CliqueStatusError;
561 localError = fetchError;
562 secnotice("clique-status", "octagon clique status errored: %@", fetchError);
563 } else {
564 octagonStatus = cliqueStatus;
565 }
566 }];
567
568 if(OctagonAuthoritativeTrustIsEnabled() || !OctagonPlatformSupportsSOS()) {
569 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) returning %@ (error: %@)",
570 configuration.useCachedAccountStatus ? "" : "non-",
571 self.ctx.context, self.ctx.altDSID,
572 OTCliqueStatusToString(octagonStatus), localError);
573 if (localError && error) {
574 *error = localError;
575 }
576 return octagonStatus;
577 }
578 }
579
580 if([OTClique platformSupportsSOS]) {
581 CFErrorRef circleStatusError = NULL;
582 sosStatus = kSOSCCError;
583 if(configuration.useCachedAccountStatus){
584 sosStatus = SOSCCThisDeviceIsInCircle(&circleStatusError);
585 } else {
586 sosStatus = SOSCCThisDeviceIsInCircleNonCached(&circleStatusError);
587 }
588 secnotice("clique-status", "sos clique status is %d (%@)", (int)sosStatus, circleStatusError);
589
590 if (error) {
591 *error = (NSError*)CFBridgingRelease(circleStatusError);
592 } else {
593 CFBridgingRelease(circleStatusError);
594 }
595 }
596 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) complete: %@",
597 configuration.useCachedAccountStatus ? "" : "non-",
598 self.ctx.context, self.ctx.altDSID,
599 OTCliqueStatusToString(octagonStatus));
600 return octagonStatus;
601 #else // !OCTAGON
602 if(error){
603 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
604 }
605 return (CliqueStatus)kSOSCCError;
606 #endif
607 }
608
609 // Don't change rules for CoreCDP, and preserve legacy behavior for now
610 // preserve old behavior until CoreCDP can move to -fetchCliqueStatus:error:
611 #define LEGACY_WAITING_BEHAVIOR (TARGET_OS_OSX || TARGET_OS_IOS)
612
613 - (CliqueStatus)fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing * _Nonnull)error
614 {
615 return [self _fetchCliqueStatus:configuration error:error];
616 }
617
618 - (CliqueStatus)fetchCliqueStatus:(NSError * __autoreleasing *)error
619 {
620 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
621 #if LEGACY_WAITING_BEHAVIOR
622 configuration.timeoutWaitForCKAccount = 0;
623 #endif
624 return [self _fetchCliqueStatus:configuration error:error];
625 }
626
627 - (CliqueStatus)cachedCliqueStatus:(BOOL)usedCached error:(NSError * __autoreleasing *)error
628 {
629 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
630 #if LEGACY_WAITING_BEHAVIOR
631 configuration.timeoutWaitForCKAccount = 0;
632 #endif
633 if (usedCached) {
634 configuration.useCachedAccountStatus = YES;
635 }
636 return [self _fetchCliqueStatus:configuration error:error];
637 }
638
639
640 - (BOOL)removeFriendsInClique:(NSArray<NSString*>*)friendIdentifiers error:(NSError * __autoreleasing *)error
641 {
642 #if OCTAGON
643 secnotice("clique-removefriends", "removeFriendsInClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
644
645 // Annoying: we must sort friendIdentifiers into octagon/sos lists.
646 NSMutableArray<NSString*>* octagonIdentifiers = [NSMutableArray array];
647 NSMutableArray<NSString*>* sosIdentifiers = [NSMutableArray array];
648
649 for(NSString* friendIdentifier in friendIdentifiers) {
650 if([friendIdentifier hasPrefix:@"SHA256:"]) {
651 [octagonIdentifiers addObject: friendIdentifier];
652 } else {
653 [sosIdentifiers addObject: friendIdentifier];
654 }
655 }
656
657 // Ensure that we don't have any peers on the wrong platform
658 if(!OctagonIsEnabled() && octagonIdentifiers.count > 0) {
659 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
660 code:errSecUnimplemented
661 userInfo:@{NSLocalizedDescriptionKey: @"Octagon is disabled; can't distrust any Octagon peers"}];
662 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
663 if(error) {
664 *error = localError;
665 }
666 return NO;
667 }
668
669 if(!OctagonPlatformSupportsSOS() && sosIdentifiers.count > 0) {
670 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
671 code:errSecUnimplemented
672 userInfo:@{NSLocalizedDescriptionKey: @"SOS is not available on this platform; can't distrust any SOS peers"}];
673 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
674 if(error) {
675 *error = localError;
676 }
677 return NO;
678 }
679
680
681 __block NSError* localError = nil;
682 bool result = true;
683
684 if(OctagonIsEnabled() && octagonIdentifiers.count > 0) {
685 OTControl* control = [self makeOTControl:error];
686 if(!control) {
687 return NO;
688 }
689
690 secnotice("clique-removefriends", "octagon: removing octagon friends: %@", octagonIdentifiers);
691
692 [control removeFriendsInClique:nil
693 context:self.ctx.context
694 peerIDs:octagonIdentifiers
695 reply:^(NSError* replyError) {
696 if(replyError) {
697 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", replyError);
698 localError = replyError;
699 } else {
700 secnotice("clique-removefriends", "octagon: friends removed: %@", octagonIdentifiers);
701 }
702 }];
703 }
704
705 if([OTClique platformSupportsSOS] && sosIdentifiers.count >0) {
706 CFErrorRef removeFriendError = NULL;
707 NSData* analyticsData = nil;
708
709 secnotice("clique-removefriends", "removing sos friends: %@", sosIdentifiers);
710
711 if(self.ctx.analytics){
712 NSError* encodingError = nil;
713 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
714 }
715
716 if(analyticsData) {
717 result = SOSCCRemovePeersFromCircleWithAnalytics((__bridge CFArrayRef)friendIdentifiers, (__bridge CFDataRef)analyticsData, &removeFriendError);
718 } else {
719 result = SOSCCRemovePeersFromCircle((__bridge CFArrayRef)friendIdentifiers, &removeFriendError);
720 }
721
722 if(removeFriendError) {
723 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", removeFriendError);
724 localError = CFBridgingRelease(removeFriendError);
725 }
726 }
727
728 if(error && localError) {
729 *error = localError;
730 }
731 secnotice("clique-removefriends", "removeFriendsInClique complete: %d", result);
732
733 return result && localError == nil;
734 #else // !OCTAGON
735 return NO;
736 #endif
737 }
738
739 - (BOOL)leaveClique:(NSError * __autoreleasing *)error
740 {
741 #if OCTAGON
742 secnotice("clique-leaveClique", "leaveClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
743 CFErrorRef removeThisDeviceError = NULL;
744 bool result = false;
745
746 if(OctagonIsEnabled()) {
747 OTControl* control = [self makeOTControl:error];
748 if(!control) {
749 return false;
750 }
751
752 __block NSError* localError = nil;
753 [control leaveClique:nil context:self.ctx.context reply:^(NSError * _Nullable leaveError) {
754 if(leaveError) {
755 secnotice("clique-leaveClique", "leaveClique errored: %@", leaveError);
756 localError = leaveError;
757 } else {
758 secnotice("clique-leaveClique", "leaveClique success.");
759 }
760 }];
761
762 if(error) {
763 *error = localError;
764 }
765 result = !localError;
766 }
767
768 if([OTClique platformSupportsSOS]) {
769 NSData* analyticsData = nil;
770
771 if(self.ctx.analytics) {
772 NSError* encodingError = nil;
773 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
774 if(!analyticsData){
775 secnotice("clique-leaveClique", "leaveClique unable to archive analytics object: %@", encodingError);
776 }
777 }
778
779 if(analyticsData) {
780 result &= SOSCCRemoveThisDeviceFromCircleWithAnalytics((__bridge CFDataRef)analyticsData, &removeThisDeviceError);
781 } else {
782 result &= SOSCCRemoveThisDeviceFromCircle(&removeThisDeviceError);
783 }
784
785 if (error) {
786 *error = (NSError*)CFBridgingRelease(removeThisDeviceError);
787 } else {
788 CFBridgingRelease(removeThisDeviceError);
789 }
790 }
791 secnotice("clique-leaveClique", "leaveClique complete: %d", result);
792
793 return result ? YES : NO;
794 #else // !OCTAGON
795 return NO;
796 #endif
797 }
798
799 - (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error
800 {
801 #if OCTAGON
802 secnotice("clique", "peerDeviceNamesByPeerID invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
803
804 NSMutableDictionary<NSString*, NSString*>* retPeers = [NSMutableDictionary dictionary];
805
806 if(OctagonIsEnabled()) {
807 OTControl* control = [self makeOTControl:error];
808 if(!control) {
809 return nil;
810 }
811
812 __block NSError* localError = nil;
813 __block NSDictionary<NSString*, NSString*>* localPeers = nil;
814
815 [control peerDeviceNamesByPeerID:nil context:OTDefaultContext reply:^(NSDictionary<NSString*,NSString*>* peers, NSError* controlError) {
816 if(controlError) {
817 secnotice("clique", "peerDeviceNamesByPeerID errored: %@", controlError);
818 } else {
819 secnotice("clique", "peerDeviceNamesByPeerID succeeded: %@", peers);
820 }
821 localError = controlError;
822 localPeers = peers;
823 }];
824
825 if(error && localError) {
826 *error = localError;
827 }
828 if(localError) {
829 return nil;
830 }
831 [retPeers addEntriesFromDictionary:localPeers];
832 secnotice("clique", "Received %lu Octagon peers", (unsigned long)localPeers.count);
833 }
834
835 if([OTClique platformSupportsSOS]) {
836 CFErrorRef peerErrorRef = NULL;
837 NSMutableDictionary<NSString*,NSString*>* peerMapping = [NSMutableDictionary dictionary];
838 NSArray* arrayOfPeerRefs = CFBridgingRelease(SOSCCCopyPeerPeerInfo(&peerErrorRef));
839 if(arrayOfPeerRefs){
840 [arrayOfPeerRefs enumerateObjectsUsingBlock:^(id peerRef, NSUInteger idx, BOOL * stop) {
841 SOSPeerInfoRef peer = (__bridge SOSPeerInfoRef)peerRef;
842 if(peer){
843 [peerMapping setObject:(__bridge NSString*)SOSPeerInfoGetPeerName(peer) forKey:(__bridge NSString*)SOSPeerInfoGetPeerID(peer)];
844 }
845 }];
846 }
847 if (error) {
848 *error = (NSError*)CFBridgingRelease(peerErrorRef);
849 } else {
850 CFBridgingRelease(peerErrorRef);
851 }
852
853 [retPeers addEntriesFromDictionary:peerMapping];
854 secnotice("clique", "Received %lu SOS peers", (unsigned long)peerMapping.count);
855 }
856
857 return retPeers;
858 #else // !OCTAGON
859 return NULL;
860 #endif
861 }
862
863 - (BOOL)joinAfterRestore:(NSError * __autoreleasing *)error
864 {
865 secnotice("clique-recovery", "joinAfterRestore for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
866 if([OTClique platformSupportsSOS]) {
867 CFErrorRef restoreError = NULL;
868
869 bool res = SOSCCRequestToJoinCircleAfterRestore(&restoreError);
870 if (error) {
871 *error = (NSError*)CFBridgingRelease(restoreError);
872 } else {
873 CFBridgingRelease(restoreError);
874 }
875 secnotice("clique-recovery", "joinAfterRestore complete: %d %@", res, error ? *error : @"no error pointer provided");
876 return res;
877 } else {
878 secnotice("clique-recovery", "SOS disabled for this platform, returning NO");
879 if(error){
880 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
881 code:errSecUnimplemented
882 userInfo:@{NSLocalizedDescriptionKey: @"join after restore unimplemented"}];
883 }
884 return NO;
885 }
886 }
887
888 - (BOOL)safariPasswordSyncingEnabled:(NSError **)error
889 {
890 secnotice("clique-safari", "safariPasswordSyncingEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
891
892 if([OTClique platformSupportsSOS]) {
893 CFErrorRef viewErrorRef = NULL;
894
895 SOSViewResultCode result = SOSCCView(kSOSViewAutofillPasswords, kSOSCCViewQuery, &viewErrorRef);
896
897 BOOL viewMember = result == kSOSCCViewMember;
898 if (error) {
899 *error = (NSError*)CFBridgingRelease(viewErrorRef);
900 } else {
901 CFBridgingRelease(viewErrorRef);
902 }
903
904 secnotice("clique-safari", "safariPasswordSyncingEnabled complete: %@", viewMember ? @"YES" : @"NO");
905
906 return viewMember;
907 } else {
908 secnotice("clique-safari", "SOS disabled for this platform, returning NO");
909 if(error){
910 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
911 code:errSecUnimplemented
912 userInfo:@{NSLocalizedDescriptionKey: @"safari password syncing enabled unimplemented"}];
913 }
914 return NO;
915 }
916 }
917
918 - (BOOL)isLastFriend:(NSError **)error
919 {
920 secnotice("clique-isLastFriend", "is last friend");
921 return NO;
922 }
923
924 - (BOOL)waitForInitialSync:(NSError *__autoreleasing*)error
925 {
926 secnotice("clique-legacy", "waitForInitialSync for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
927 if([OTClique platformSupportsSOS]) {
928 CFErrorRef initialSyncErrorRef = NULL;
929 bool result = false;
930 if(self.ctx.analytics){
931 NSError* encodingError = nil;
932 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
933 if(!encodingError && analyticsData){
934 result = SOSCCWaitForInitialSyncWithAnalytics((__bridge CFDataRef)analyticsData, &initialSyncErrorRef);
935 }else{
936 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
937 }
938 }else{
939 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
940 }
941
942 BOOL initialSyncResult = (result == true);
943 if (error) {
944 *error = (NSError*)CFBridgingRelease(initialSyncErrorRef);
945 } else {
946 CFBridgingRelease(initialSyncErrorRef);
947 }
948 secnotice("clique-legacy", "waitForInitialSync waited: %d %@", initialSyncResult, error ? *error : @"no error pointer provided");
949 return initialSyncResult;
950 } else {
951 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
952 if(error){
953 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
954 code:errSecUnimplemented
955 userInfo:@{NSLocalizedDescriptionKey: @"wait for initial sync unimplemented"}];
956 }
957 return NO;
958 }
959 }
960
961 - (NSArray* _Nullable)copyViewUnawarePeerInfo:(NSError *__autoreleasing*)error
962 {
963 secnotice("clique-legacy", "copyViewUnawarePeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
964
965 if([OTClique platformSupportsSOS]) {
966 CFErrorRef copyViewUnawarePeerInfoErrorRef = NULL;
967 CFArrayRef peerListRef = SOSCCCopyViewUnawarePeerInfo(&copyViewUnawarePeerInfoErrorRef);
968
969 NSArray* peerList = (peerListRef ? (NSArray*)(CFBridgingRelease(peerListRef)) : nil);
970 if (error) {
971 *error = (NSError*)CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
972 } else {
973 CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
974 }
975 return peerList;
976 } else {
977 secnotice("clique-legacy", "SOS disabled for this platform, returning NULL");
978 if(error){
979 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
980 code:errSecUnimplemented
981 userInfo:@{NSLocalizedDescriptionKey: @"copy view unaware peer info unimplemented"}];
982 }
983 return nil;
984 }
985 }
986
987 - (BOOL)viewSet:(NSSet*)enabledViews disabledViews:(NSSet*)disabledViews
988 {
989 secnotice("clique-legacy", "viewSet for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
990 if([OTClique platformSupportsSOS]) {
991 bool result = false;
992 if(self.ctx.analytics){
993 NSError* encodingError = nil;
994 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
995 if(!encodingError && analyticsData){
996 result = SOSCCViewSetWithAnalytics((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews, (__bridge CFDataRef)analyticsData);
997 }else{
998 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
999 }
1000 }else{
1001 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
1002 }
1003
1004 BOOL viewSetResult = (result == true);
1005 return viewSetResult;
1006 } else {
1007 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1008 return NO;
1009 }
1010 }
1011
1012 - (BOOL)setUserCredentialsAndDSID:(NSString*)userLabel
1013 password:(NSData*)userPassword
1014 error:(NSError *__autoreleasing*)error
1015 {
1016 secnotice("clique-legacy", "setUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1017 if([OTClique platformSupportsSOS]) {
1018 CFErrorRef setCredentialsErrorRef = NULL;
1019 bool result = false;
1020 if(self.ctx.analytics){
1021 NSError* encodingError = nil;
1022 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1023 if(!encodingError && analyticsData){
1024 result = SOSCCSetUserCredentialsAndDSIDWithAnalytics((__bridge CFStringRef)userLabel,
1025 (__bridge CFDataRef)userPassword,
1026 (__bridge CFStringRef)self.ctx.dsid,
1027 (__bridge CFDataRef)analyticsData,
1028 &setCredentialsErrorRef);
1029 }else{
1030 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1031 (__bridge CFDataRef)userPassword,
1032 (__bridge CFStringRef)self.ctx.dsid,
1033 &setCredentialsErrorRef);
1034 }
1035 }else{
1036 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1037 (__bridge CFDataRef)userPassword,
1038 (__bridge CFStringRef)self.ctx.dsid,
1039 &setCredentialsErrorRef);
1040 }
1041
1042 BOOL setCredentialsResult = (result == true);
1043 if (error) {
1044 *error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
1045 } else {
1046 CFBridgingRelease(setCredentialsErrorRef);
1047 }
1048 secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
1049 return setCredentialsResult;
1050 } else {
1051 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1052 if(error){
1053 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1054 code:errSecUnimplemented
1055 userInfo:@{NSLocalizedDescriptionKey: @"set user credentials unimplemented"}];
1056 }
1057 return NO;
1058 }
1059 }
1060
1061 - (BOOL)tryUserCredentialsAndDSID:(NSString*)userLabel
1062 password:(NSData*)userPassword
1063 error:(NSError *__autoreleasing*)error
1064 {
1065 secnotice("clique-legacy", "tryUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1066
1067 if([OTClique platformSupportsSOS]) {
1068 CFErrorRef tryCredentialsErrorRef = NULL;
1069 bool result = SOSCCTryUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1070 (__bridge CFDataRef)userPassword,
1071 (__bridge CFStringRef)self.ctx.dsid,
1072 &tryCredentialsErrorRef);
1073
1074 BOOL tryCredentialsResult = (result == true);
1075 if (error) {
1076 *error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
1077 } else {
1078 CFBridgingRelease(tryCredentialsErrorRef);
1079 }
1080 secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
1081 return tryCredentialsResult;
1082 } else {
1083 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1084 if(error){
1085 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1086 code:errSecUnimplemented
1087 userInfo:@{NSLocalizedDescriptionKey: @"try user credentials unimplemented"}];
1088 }
1089 return NO;
1090 }
1091 }
1092
1093 - (NSArray* _Nullable)copyPeerPeerInfo:(NSError *__autoreleasing*)error
1094 {
1095 secnotice("clique-legacy", "copyPeerPeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1096
1097 if([OTClique platformSupportsSOS]) {
1098 CFErrorRef copyPeerErrorRef = NULL;
1099 CFArrayRef result = SOSCCCopyPeerPeerInfo(&copyPeerErrorRef);
1100
1101 NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
1102
1103 if (error) {
1104 *error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
1105 } else {
1106 CFBridgingRelease(copyPeerErrorRef);
1107 }
1108 secnotice("clique-legacy", "copyPeerPeerInfo results: %@", peerList);
1109
1110 return peerList;
1111 } else {
1112 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1113 if(error){
1114 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1115 code:errSecUnimplemented
1116 userInfo:@{NSLocalizedDescriptionKey: @"copy peer peer info unimplemented"}];
1117 }
1118 return nil;
1119 }
1120 }
1121
1122 - (BOOL)peersHaveViewsEnabled:(NSArray<NSString*>*)viewNames error:(NSError *__autoreleasing*)error
1123 {
1124 secnotice("clique-legacy", "peersHaveViewsEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1125
1126 if([OTClique platformSupportsSOS]) {
1127 CFErrorRef viewsEnabledErrorRef = NULL;
1128 BOOL viewsEnabledResult = NO;
1129
1130 CFBooleanRef result = SOSCCPeersHaveViewsEnabled((__bridge CFArrayRef)viewNames, &viewsEnabledErrorRef);
1131 if(result){
1132 viewsEnabledResult = CFBooleanGetValue(result);
1133 }
1134 if (error) {
1135 *error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
1136 } else {
1137 CFBridgingRelease(viewsEnabledErrorRef);
1138 }
1139 secnotice("clique-legacy", "peersHaveViewsEnabled results: %@", viewsEnabledResult ? @"YES" : @"NO");
1140
1141 return viewsEnabledResult;
1142 } else {
1143 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1144 if(error){
1145 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1146 code:errSecUnimplemented
1147 userInfo:@{NSLocalizedDescriptionKey: @"peers have views enabled unimplemented"}];
1148 }
1149 return NO;
1150 }
1151 }
1152
1153 - (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
1154 {
1155 bool result = false;
1156 CFErrorRef joinErrorRef = NULL;
1157
1158 #if OCTAGON
1159 secnotice("clique-legacy", "requestToJoinCircle for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1160
1161 if(OctagonIsEnabled()) {
1162 NSError* localError = nil;
1163 [self resetAndEstablish:CuttlefishResetReasonLegacyJoinCircle error:&localError];
1164
1165 if(localError) {
1166 secnotice("clique-legacy", "account reset failed: %@", localError);
1167 if(error) {
1168 *error = localError;
1169 }
1170 return NO;
1171 } else {
1172 secnotice("clique-legacy", "account reset succeeded");
1173 }
1174
1175 // If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
1176 if(!OctagonPlatformSupportsSOS()) {
1177 secnotice("clique-legacy", "sos requestToJoinCircle results: %d %@", result, joinErrorRef);
1178 return YES;
1179 }
1180 }
1181 #endif // OCTAGON
1182
1183 if([OTClique platformSupportsSOS]) {
1184 NSData* analyticsData = nil;
1185 if(self.ctx.analytics){
1186 NSError* encodingError = nil;
1187 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1188 }
1189
1190 if(analyticsData){
1191 result = SOSCCRequestToJoinCircleWithAnalytics((__bridge CFDataRef)analyticsData, &joinErrorRef);
1192 } else {
1193 result = SOSCCRequestToJoinCircle(&joinErrorRef);
1194 }
1195
1196 secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
1197 }
1198
1199 if (error) {
1200 *error = (NSError*)CFBridgingRelease(joinErrorRef);
1201 } else {
1202 CFBridgingRelease(joinErrorRef);
1203 }
1204 return result ? YES : NO;
1205 }
1206
1207 - (BOOL)accountUserKeyAvailable
1208 {
1209 secnotice("clique-legacy", "accountUserKeyAvailable for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1210
1211 if([OTClique platformSupportsSOS]) {
1212 BOOL canAuthenticate = (BOOL)SOSCCCanAuthenticate(NULL);
1213 if (canAuthenticate == NO) {
1214 secnotice("clique-legacy", "Security requires credentials...");
1215 }
1216 return canAuthenticate;
1217 } else {
1218 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1219 return NO;
1220 }
1221 }
1222
1223 // MARK: SBD interfaces
1224 + (OTBottleIDs* _Nullable)findOptimalBottleIDsWithContextData:(OTConfigurationContext*)data
1225 error:(NSError**)error
1226 {
1227 #if OCTAGON
1228 secnotice("clique-findbottle", "finding optimal bottles for context:%@, altdsid:%@", data.context, data.altDSID);
1229
1230 if(OctagonIsEnabled()) {
1231 __block NSError* localError = nil;
1232 __block NSArray<NSString*>* localViableBottleIDs = nil;
1233 __block NSArray<NSString*>* localPartiallyViableBottleIDs = nil;
1234
1235 OTControl *control = [data makeOTControl:&localError];
1236 if (!control) {
1237 secnotice("clique-findbottle", "unable to create otcontrol: %@", localError);
1238 if (error) {
1239 *error = localError;
1240 }
1241 return nil;
1242 }
1243 [control fetchAllViableBottles:OTCKContainerName
1244 context:data.context
1245 reply:^(NSArray<NSString *> * _Nullable sortedBottleIDs,
1246 NSArray<NSString*> * _Nullable sortedPartialBottleIDs,
1247 NSError * _Nullable fetchError) {
1248 if(fetchError) {
1249 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData errored: %@", fetchError);
1250 } else {
1251 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData succeeded: %@, %@", sortedBottleIDs, sortedPartialBottleIDs);
1252 }
1253 localError = fetchError;
1254 localViableBottleIDs = sortedBottleIDs;
1255 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
1256 }];
1257
1258 if(error && localError) {
1259 *error = localError;
1260 }
1261 OTBottleIDs* bottleIDs = [[OTBottleIDs alloc] init];
1262 bottleIDs.preferredBottleIDs = localViableBottleIDs;
1263 bottleIDs.partialRecoveryBottleIDs = localPartiallyViableBottleIDs;
1264
1265 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData complete");
1266
1267 return bottleIDs;
1268 } else {
1269 // With octagon off, fail with 'unimplemented'
1270 if(error) {
1271 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1272 code:errSecUnimplemented
1273 userInfo:@{NSLocalizedDescriptionKey: @"optimal bottle IDs unimplemented"}];
1274 }
1275 return nil;
1276 }
1277 #else // !OCTAGON
1278 if (error) {
1279 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1280 }
1281 return NULL;
1282 #endif
1283 }
1284
1285 + (OTClique* _Nullable)recoverWithContextData:(OTConfigurationContext*)data
1286 bottleID:(NSString*)bottleID
1287 escrowedEntropy:(NSData*)entropy
1288 error:(NSError**)error
1289 {
1290 #if OCTAGON
1291 secnotice("octagon", "replaced by performEscrowRecoveryWithContextData:escrowArguments:error: remove call");
1292 return nil;
1293 #else // !OCTAGON
1294 if (error) {
1295 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1296 }
1297 return NULL;
1298 #endif
1299 }
1300
1301 // used by sbd to fill in the escrow record
1302 // TODO: what extra entitlement do you need to call this?
1303 - (void)fetchEscrowContents:(void (^)(NSData* _Nullable entropy,
1304 NSString* _Nullable bottleID,
1305 NSData* _Nullable signingPublicKey,
1306 NSError* _Nullable error))reply
1307 {
1308 #if OCTAGON
1309 secnotice("clique-fetchescrow", "fetching entropy for bottling for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1310
1311 if(OctagonIsEnabled()) {
1312 NSError* controlError = nil;
1313 OTControl* control = [self makeOTControl:&controlError];
1314 if (!control) {
1315 reply(nil, nil, nil, controlError);
1316 return;
1317 }
1318 [control fetchEscrowContents:OTCKContainerName
1319 contextID:self.ctx.context
1320 reply:^(NSData * _Nullable entropy,
1321 NSString * _Nullable bottleID,
1322 NSData * _Nullable signingPublicKey,
1323 NSError * _Nullable error) {
1324 if(error){
1325 secnotice("clique-fetchescrow", "fetchEscrowContents errored: %@", error);
1326 } else{
1327 secnotice("clique-fetchescrow","fetchEscrowContents succeeded");
1328 }
1329 reply (entropy, bottleID, signingPublicKey, error);
1330 }];
1331
1332 return;
1333 } else {
1334 // With octagon off, fail with 'unimplemented'
1335 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain
1336 code:errSecUnimplemented
1337 userInfo:@{NSLocalizedDescriptionKey: @"fetchEscrowRecordContents unimplemented"}]);
1338 }
1339 #else // !OCTAGON
1340 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1341 #endif
1342 }
1343
1344 + (void)setNewRecoveryKeyWithData:(OTConfigurationContext *)ctx
1345 recoveryKey:(NSString*)recoveryKey reply:(nonnull void (^)(SecRecoveryKey *rk, NSError *error))reply
1346 {
1347 #if OCTAGON
1348 secnotice("octagon-setrecoverykey", "setNewRecoveryKeyWithData invoked for context: %@", ctx.context);
1349 //set the recovery key for SOS
1350 NSError* createRecoveryKeyError = nil;
1351 NSMutableDictionary *userInfo = [NSMutableDictionary new];
1352 NSError* retError = nil;
1353
1354 SecRecoveryKey *rk = SecRKCreateRecoveryKeyWithError(recoveryKey, &createRecoveryKeyError);
1355 if (rk == nil) {
1356 secerror("octagon-setrecoverykey, SecRKCreateRecoveryKeyWithError() failed: %@", createRecoveryKeyError);
1357 userInfo[NSLocalizedDescriptionKey] = @"SecRKCreateRecoveryKeyWithError() failed";
1358 userInfo[NSUnderlyingErrorKey] = createRecoveryKeyError;
1359 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1360 reply(nil, retError);
1361 return;
1362 }
1363 if([OTClique platformSupportsSOS]) {
1364 CFErrorRef registerError = nil;
1365 if (!SecRKRegisterBackupPublicKey(rk, &registerError)) {
1366 secerror("octagon-setrecoverykey, SecRKRegisterBackupPublicKey() failed: %@", registerError);
1367 NSError *underlyingError = CFBridgingRelease(registerError);
1368 userInfo[NSLocalizedDescriptionKey] = @"SecRKRegisterBackupPublicKey() failed";
1369 userInfo[NSUnderlyingErrorKey] = underlyingError;
1370 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1371 reply(nil,retError);
1372 return;
1373 } else {
1374 secnotice("octagon-setrecoverykey", "successfully registered recovery key for SOS");
1375 }
1376 }
1377
1378 //set the recovery key for Octagon
1379 if(OctagonRecoveryKeyIsEnabled()) {
1380 NSError* controlError = nil;
1381 OTControl* control = [ctx makeOTControl:&controlError];
1382 if(!control) {
1383 secnotice("octagon-setrecoverykey", "failed to fetch OTControl object: %@", controlError);
1384 reply(nil, controlError);
1385 return;
1386 }
1387 [control createRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError * createError) {
1388 if(createError){
1389 secerror("octagon-setrecoverykey, failed to create octagon recovery key");
1390 reply(nil, createError);
1391 return;
1392 } else {
1393 secnotice("octagon-setrecoverykey", "successfully set octagon recovery key");
1394 reply(rk, nil);
1395 return;
1396 }
1397 }];
1398 }
1399 #else // !OCTAGON
1400 reply(nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1401 #endif
1402 }
1403
1404 + (void)recoverOctagonUsingData:(OTConfigurationContext *)ctx
1405 recoveryKey:(NSString*)recoveryKey
1406 reply:(void(^)(NSError* _Nullable error))reply
1407 {
1408 #if OCTAGON
1409 if(OctagonRecoveryKeyIsEnabled()) {
1410 NSError* controlError = nil;
1411 OTControl* control = [ctx makeOTControl:&controlError];
1412
1413 secnotice("clique-recoverykey", "join using recovery key");
1414
1415 if(!control) {
1416 secnotice("clique-recoverykey", "failed to fetch OTControl object: %@", controlError);
1417 reply(controlError);
1418 return;
1419 }
1420 [control joinWithRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError *joinError) {
1421 if(joinError){
1422 secnotice("clique-recoverykey", "failed to join using recovery key: %@", joinError);
1423 reply(joinError);
1424 return;
1425 }
1426 secnotice("clique-recoverykey", "successfully joined using recovery key");
1427 reply(nil);
1428 }];
1429 }
1430
1431 #else // !OCTAGON
1432 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1433 #endif
1434 }
1435
1436 - (void)performedCDPStateMachineRun:(OTCliqueCDPContextType)type
1437 success:(BOOL)success
1438 error:(NSError * _Nullable)error
1439 reply:(void(^)(NSError* _Nullable error))reply
1440 {
1441 #if OCTAGON
1442 NSError* controlError = nil;
1443 OTControl* control = [self makeOTControl:&controlError];
1444 if(!control) {
1445 secnotice("clique-cdp-sm", "octagon, failed to fetch OTControl object: %@", controlError);
1446 reply(controlError);
1447 return;
1448 }
1449
1450 [control postCDPFollowupResult:success type:type error:error containerName:OTCKContainerName contextName:OTDefaultContext reply:^(NSError *postError) {
1451 if(postError){
1452 secnotice("clique-cdp-sm", "failed to post %@ result: %@ ", type, postError);
1453 reply(postError);
1454 return;
1455 }
1456 if (success) {
1457 secnotice("clique-cdp-sm", "posted success: %@", type);
1458 } else {
1459 secnotice("clique-cdp-sm", "posted error: %@: %@", type, error);
1460 }
1461 reply(NULL);
1462 }];
1463 #else // !OCTAGON
1464 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1465 #endif
1466 }
1467
1468 - (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error
1469 {
1470 #if OCTAGON
1471 OTControl* control = nil;
1472
1473 if (!OctagonIsEnabled()) {
1474 secnotice("clique-waitforoctagonupgrade", "cannot upgrade, octagon is not enabled");
1475 if (error) {
1476 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:@{NSLocalizedDescriptionKey: @"Octagon is not enabled"}];
1477 }
1478 return NO;
1479 }
1480
1481 NSError *controlError = nil;
1482 control = [self makeOTControl:&controlError];
1483 if (!control) {
1484 secnotice("clique-waitforoctagonupgrade", "octagon, failed to fetch OTControl object: %@", controlError);
1485 if (error) {
1486 *error = controlError;
1487 }
1488 return NO;
1489 }
1490
1491 __block BOOL ret = NO;
1492 __block NSError* blockError = nil;
1493
1494 [control waitForOctagonUpgrade:OTCKContainerName context:OTDefaultContext reply:^(NSError *postError) {
1495 if(postError){
1496 secnotice("clique-waitforoctagonupgrade", "error from control: %@", postError);
1497 blockError = postError;
1498 ret = NO;
1499 } else {
1500 secnotice("clique-waitforoctagonupgrade", "successfully upgraded to octagon");
1501 ret = YES;
1502 }
1503 }];
1504
1505 if (blockError && error) {
1506 *error = blockError;
1507 }
1508
1509 return ret;
1510 #else // !OCTAGON
1511 if(error) {
1512 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1513 }
1514 return NO;
1515 #endif
1516 }
1517
1518 - (void)performedFailureCDPStateMachineRun:(OTCliqueCDPContextType)type
1519 error:(NSError * _Nullable)error
1520 reply:(void(^)(NSError* _Nullable error))reply
1521 {
1522 [self performedCDPStateMachineRun:type success:NO error:error reply:reply];
1523 }
1524
1525 - (void)performedSuccessfulCDPStateMachineRun:(OTCliqueCDPContextType)type
1526 reply:(void(^)(NSError* _Nullable error))reply
1527 {
1528 [self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
1529 }
1530
1531 @end
1532
1533 #endif /* OBJC2 */