/* * Copyright (c) 2017 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #if __OBJC2__ #import <Foundation/NSXPCConnection_Private.h> #import <xpc/xpc.h> #import <Security/SecItemPriv.h> #import <Security/SecXPCHelper.h> #import "keychain/ot/OTClique.h" #import "keychain/ot/OTControl.h" #import "keychain/ot/OTDefines.h" #import "keychain/ot/OTControlProtocol.h" #import "keychain/ot/OctagonControlServer.h" #include <security_utilities/debugging.h> #if OCTAGON #import <SecurityFoundation/SFKey.h> #endif @interface OTControl () @property NSXPCConnection *connection; @property bool sync; @end @implementation OTControl - (instancetype)initWithConnection:(NSXPCConnection*)connection sync:(bool)sync { if(self = [super init]) { _connection = connection; _sync = sync; } return self; } - (void)dealloc { [self.connection invalidate]; } - (NSXPCConnection<OTControlProtocol>*)getConnection:(void (^)(NSError *error))handler { if(self.sync) { return [self.connection synchronousRemoteObjectProxyWithErrorHandler: handler]; } else { return [self.connection remoteObjectProxyWithErrorHandler: handler]; } } - (void)restore:(NSString *)contextID dsid:(NSString *)dsid secret:(NSData*)secret escrowRecordID:(NSString*)escrowRecordID reply:(void (^)(NSData* signingKeyData, NSData* encryptionKeyData, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(nil, nil, error); }] restore:contextID dsid:dsid secret:secret escrowRecordID:escrowRecordID reply:^(NSData* signingKeyData, NSData* encryptionKeyData, NSError *error) { reply(signingKeyData, encryptionKeyData, error); }]; } -(void)reset:(void (^)(BOOL result, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(NO, error); }] reset:^(BOOL result, NSError * _Nullable error) { reply(result, error); }]; } - (void)signingKey:(void (^)(NSData* result, NSError* _Nullable error))reply { [self octagonSigningPublicKey:reply]; } - (void)octagonSigningPublicKey:(nonnull void (^)(NSData * _Nullable, NSError * _Nullable))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] octagonSigningPublicKey:^(NSData *signingKey, NSError * _Nullable error) { reply(signingKey, error); }]; } - (void)encryptionKey:(void (^)(NSData* result, NSError* _Nullable error))reply { [self octagonEncryptionPublicKey:reply]; } - (void)octagonEncryptionPublicKey:(nonnull void (^)(NSData * _Nullable, NSError * _Nullable))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] octagonEncryptionPublicKey:^(NSData *encryptionKey, NSError * _Nullable error) { reply(encryptionKey, error); }]; } - (void)listOfRecords:(void (^)(NSArray* list, NSError* _Nullable error))reply { [self listOfEligibleBottledPeerRecords:reply]; } - (void)listOfEligibleBottledPeerRecords:(nonnull void (^)(NSArray * _Nullable, NSError * _Nullable))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] listOfEligibleBottledPeerRecords:^(NSArray *list, NSError * _Nullable error) { reply(list, error); }]; } - (void)signIn:(NSString*)altDSID container:(NSString* _Nullable)container context:(NSString*)contextID reply:(void (^)(NSError * _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] signIn:altDSID container:container context:contextID reply:^(NSError * _Nullable error) { reply(error); }]; } - (void)signOut:(NSString* _Nullable)container context:(NSString*)contextID reply:(void (^)(NSError * _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] signOut:container context:contextID reply:^(NSError * _Nullable error) { reply(error); }]; } - (void)notifyIDMSTrustLevelChangeForContainer:(NSString* _Nullable)container context:(NSString*)contextID reply:(void (^)(NSError * _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] notifyIDMSTrustLevelChangeForContainer:container context:contextID reply:^(NSError * _Nullable error) { reply(error); }]; } - (void)handleIdentityChangeForSigningKey:(SFECKeyPair* _Nonnull)peerSigningKey ForEncryptionKey:(SFECKeyPair* _Nonnull)encryptionKey ForPeerID:(NSString*)peerID reply:(void (^)(BOOL result, NSError* _Nullable error))reply { #if OCTAGON [[self getConnection: ^(NSError* error) { reply(NO, error); }] handleIdentityChangeForSigningKey:peerSigningKey ForEncryptionKey:encryptionKey ForPeerID:peerID reply:^(BOOL result, NSError* _Nullable error) { reply(result, error); }]; #else reply(NO, NULL); #endif } - (void)rpcEpochWithConfiguration:(OTJoiningConfiguration*)config reply:(void (^)(uint64_t epoch, NSError * _Nullable error))reply { #if OCTAGON [[self getConnection: ^(NSError* error) { reply(0, error); }] rpcEpochWithConfiguration:config reply:^(uint64_t epoch, NSError * _Nullable error) { reply(epoch, error); }]; #else reply(0, NULL); #endif } - (void)rpcPrepareIdentityAsApplicantWithConfiguration:(OTJoiningConfiguration*)config reply:(void (^)(NSString * _Nullable peerID, NSData * _Nullable permanentInfo, NSData * _Nullable permanentInfoSig, NSData * _Nullable stableInfo, NSData * _Nullable stableInfoSig, NSError * _Nullable error))reply { #if OCTAGON [[self getConnection: ^(NSError* error) { reply(nil, nil, nil, nil, nil, error); }] rpcPrepareIdentityAsApplicantWithConfiguration:config reply:^(NSString* pID, NSData* pI, NSData* piSig, NSData* si, NSData* siSig, NSError* e) { reply(pID, pI, piSig, si, siSig, e); }]; #else reply(NULL, NULL, NULL, NULL, NULL, NULL); #endif } - (void)rpcVoucherWithConfiguration:(OTJoiningConfiguration*)config peerID:(NSString*)peerID permanentInfo:(NSData *)permanentInfo permanentInfoSig:(NSData *)permanentInfoSig stableInfo:(NSData *)stableInfo stableInfoSig:(NSData *)stableInfoSig reply:(void (^)(NSData* voucher, NSData* voucherSig, NSError * _Nullable error))reply { #if OCTAGON [[self getConnection: ^(NSError* error) { reply(nil, nil, error); }] rpcVoucherWithConfiguration:config peerID:peerID permanentInfo:permanentInfo permanentInfoSig:permanentInfoSig stableInfo:stableInfo stableInfoSig:stableInfoSig reply:^(NSData* voucher, NSData* voucherSig, NSError * _Nullable error) { reply(voucher, voucherSig, error); }]; #else reply(NULL, NULL, NULL); #endif } - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config vouchData:(NSData*)vouchData vouchSig:(NSData*)vouchSig reply:(void (^)(NSError * _Nullable error))reply { #if OCTAGON [[self getConnection: ^(NSError* error) { reply(error); }] rpcJoinWithConfiguration:config vouchData:vouchData vouchSig:vouchSig reply:^(NSError* e) { reply(e); }]; #else reply(NULL); #endif } - (void)preflightBottledPeer:(NSString*)contextID dsid:(NSString*)dsid reply:(void (^)(NSData* _Nullable entropy, NSString* _Nullable bottleID, NSData* _Nullable signingPublicKey, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(nil, nil, nil, error); }] preflightBottledPeer:contextID dsid:dsid reply:^(NSData* _Nullable entropy, NSString* _Nullable bottleID, NSData* _Nullable signingPublicKey, NSError* _Nullable error) { reply(entropy, bottleID, signingPublicKey, error); }]; } - (void)launchBottledPeer:(NSString*)contextID bottleID:(NSString*)bottleID reply:(void (^ _Nullable)(NSError* _Nullable))reply { [[self getConnection: ^(NSError* error) { reply(error); }] launchBottledPeer:contextID bottleID:bottleID reply:^(NSError * _Nullable error) { reply(error); }]; } - (void)scrubBottledPeer:(NSString*)contextID bottleID:(NSString*)bottleID reply:(void (^ _Nullable)(NSError* _Nullable))reply { [[self getConnection: ^(NSError* error) { reply(error); }] scrubBottledPeer:contextID bottleID:bottleID reply:reply]; } - (void)status:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSDictionary* _Nullable result, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] status:container context:context reply:reply]; } - (void)fetchEgoPeerID:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSString* _Nullable peerID, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] fetchEgoPeerID:container context:context reply:reply]; } - (void)fetchCliqueStatus:(NSString* _Nullable)container context:(NSString*)context configuration:(OTOperationConfiguration*)configuration reply:(void (^)(CliqueStatus cliqueStatus, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(CliqueStatusError, error); }] fetchCliqueStatus:container context:context configuration:configuration reply:reply]; } - (void)fetchTrustStatus:(NSString* _Nullable)container context:(NSString*)context configuration:(OTOperationConfiguration *)configuration reply:(void (^)(CliqueStatus status, NSString* peerID, NSNumber * _Nullable numberOfOctagonPeers, BOOL isExcluded, NSError * _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(CliqueStatusError, false, NULL, false, error); }] fetchTrustStatus:container context:context configuration:configuration reply:reply]; } - (void)startOctagonStateMachine:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] startOctagonStateMachine:container context:context reply:reply]; } - (void)resetAndEstablish:(NSString* _Nullable)container context:(NSString*)context altDSID:(NSString*)altDSID resetReason:(CuttlefishResetReason)resetReason reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] resetAndEstablish:container context:context altDSID:altDSID resetReason:resetReason reply:reply]; } - (void)establish:(NSString* _Nullable)container context:(NSString*)context altDSID:(NSString*)altDSID reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] establish:container context:context altDSID:altDSID reply:reply]; } - (void)leaveClique:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] leaveClique:container context:context reply:reply]; } - (void)removeFriendsInClique:(NSString* _Nullable)container context:(NSString*)context peerIDs:(NSArray<NSString*>*)peerIDs reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] removeFriendsInClique:container context:context peerIDs:peerIDs reply:reply]; } - (void)peerDeviceNamesByPeerID:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSDictionary<NSString*, NSString*>* _Nullable peers, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(nil, error); }] peerDeviceNamesByPeerID:container context:context reply:reply]; } - (void)fetchAllViableBottles:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSArray<NSString*>* _Nullable sortedBottleIDs, NSArray<NSString*> * _Nullable sortedPartialBottleIDs, NSError* _Nullable error))reply { [[self getConnection:^(NSError *error) { reply(nil, nil, error); }] fetchAllViableBottles:container context:context reply:reply]; } -(void)restore:(NSString* _Nullable)containerName contextID:(NSString *)contextID bottleSalt:(NSString *)bottleSalt entropy:(NSData *)entropy bottleID:(NSString *)bottleID reply:(void (^)(NSError * _Nullable))reply { [[self getConnection:^(NSError *error) { reply(error); }] restore:containerName contextID:contextID bottleSalt:bottleSalt entropy:entropy bottleID:bottleID reply:reply]; } - (void)fetchEscrowContents:(NSString* _Nullable)containerName contextID:(NSString *)contextID reply:(void (^)(NSData* _Nullable entropy, NSString* _Nullable bottleID, NSData* _Nullable signingPublicKey, NSError* _Nullable error))reply { [[self getConnection:^(NSError *error) { reply(nil, nil, nil, error); }] fetchEscrowContents:containerName contextID:contextID reply:reply]; } - (void) createRecoveryKey:(NSString* _Nullable)containerName contextID:(NSString *)contextID recoveryKey:(NSString *)recoveryKey reply:(void (^)( NSError * error))reply { [[self getConnection:^(NSError *error) { reply(error); }] createRecoveryKey:containerName contextID:contextID recoveryKey:recoveryKey reply:reply]; } - (void) joinWithRecoveryKey:(NSString* _Nullable)containerName contextID:(NSString *)contextID recoveryKey:(NSString*)recoveryKey reply:(void (^)(NSError * _Nullable))reply { [[self getConnection:^(NSError *error) { reply(error); }] joinWithRecoveryKey:containerName contextID:contextID recoveryKey:recoveryKey reply:reply]; } - (void)healthCheck:(NSString *)container context:(NSString *)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *_Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] healthCheck:container context:context skipRateLimitingCheck:skipRateLimitingCheck reply:reply]; } - (void)waitForOctagonUpgrade:(NSString* _Nullable)container context:(NSString*)context reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] waitForOctagonUpgrade:container context:context reply:reply]; } - (void)postCDPFollowupResult:(BOOL)success type:(OTCliqueCDPContextType)type error:(NSError * _Nullable)error containerName:(NSString* _Nullable)containerName contextName:(NSString *)contextName reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(connectionError); }] postCDPFollowupResult:success type:type error:[SecXPCHelper cleanseErrorForXPC:error] containerName:containerName contextName:contextName reply:reply]; } - (void)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(connectionError); }] tapToRadar:action description:description radar:radar reply:reply]; } - (void)refetchCKKSPolicy:(NSString* _Nullable)container contextID:(NSString*)contextID reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* error) { reply(error); }] refetchCKKSPolicy:container contextID:contextID reply:reply]; } - (void)setCDPEnabled:(NSString* _Nullable)containerName contextID:(NSString*)contextID reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(connectionError); }] setCDPEnabled:containerName contextID:contextID reply:reply]; } - (void)getCDPStatus:(NSString* _Nullable)containerName contextID:(NSString*)contextID reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(OTCDPStatusUnknown, connectionError); }] getCDPStatus:containerName contextID:contextID reply:reply]; } - (void)fetchEscrowRecords:(NSString * _Nullable)container contextID:(NSString*)contextID forceFetch:(BOOL)forceFetch reply:(void (^)(NSArray<NSData*>* _Nullable records, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(nil, connectionError); }] fetchEscrowRecords:container contextID:contextID forceFetch:forceFetch reply:reply]; } - (void)setUserControllableViewsSyncStatus:(NSString* _Nullable)containerName contextID:(NSString*)contextID enabled:(BOOL)enabled reply:(void (^)(BOOL nowSyncing, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(NO, connectionError); }] setUserControllableViewsSyncStatus:containerName contextID:contextID enabled:enabled reply:reply]; } - (void)fetchUserControllableViewsSyncStatus:(NSString* _Nullable)containerName contextID:(NSString*)contextID reply:(void (^)(BOOL nowSyncing, NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(NO, connectionError); }] fetchUserControllableViewsSyncStatus:containerName contextID:contextID reply:reply]; } - (void)invalidateEscrowCache:(NSString * _Nullable)containerName contextID:(NSString*)contextID reply:(nonnull void (^)(NSError * _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(connectionError); }] invalidateEscrowCache:containerName contextID:contextID reply:reply]; } - (void)resetAccountCDPContents:(NSString* _Nullable)containerName contextID:(NSString*)contextID reply:(void (^)(NSError* _Nullable error))reply { [[self getConnection: ^(NSError* connectionError) { reply(connectionError); }] resetAccountCDPContents:containerName contextID:contextID reply:reply]; } + (OTControl*)controlObject:(NSError* __autoreleasing *)error { return [OTControl controlObject:false error:error]; } + (OTControl*)controlObject:(bool)sync error:(NSError**)error { NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydOctagonServiceName) options:0]; if (connection == nil) { if(error) { *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecInternalError userInfo:@{NSLocalizedDescriptionKey: @"Couldn't create connection (no reason given)"}]; } return nil; } NSXPCInterface *interface = OTSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(OTControlProtocol)]); connection.remoteObjectInterface = interface; [connection resume]; OTControl* c = [[OTControl alloc] initWithConnection:connection sync:sync]; return c; } @end #endif // __OBJC2__