// // AgentConnection.m // Sparkle // // Created by Mayur Pawashe on 7/17/16. // Copyright © 2016 Sparkle Project. All rights reserved. // #import "AgentConnection.h" #import "SPUMessageTypes.h" #import "SPUInstallerAgentProtocol.h" #import "SUInstallerAgentInitiationProtocol.h" #import "SUCodeSigningVerifier.h" #import "SULog.h" #include "AppKitPrevention.h" @interface AgentConnection () @end @implementation AgentConnection { NSXPCListener *_xpcListener; NSXPCConnection *_activeConnection; __weak id _delegate; } @synthesize agent = _agent; @synthesize connected = _connected; @synthesize invalidationError = _invalidationError; - (instancetype)initWithHostBundleIdentifier:(NSString *)bundleIdentifier delegate:(id)delegate { self = [super init]; if (self != nil) { // Agents should always be the one that connect to daemons due to how mach bootstraps work // For this reason, we are the ones that are creating a listener, not the agent _xpcListener = [[NSXPCListener alloc] initWithMachServiceName:SPUProgressAgentServiceNameForBundleIdentifier(bundleIdentifier)]; _xpcListener.delegate = self; _delegate = delegate; } return self; } - (void)startListener { [_xpcListener resume]; } - (void)invalidate { _delegate = nil; [_activeConnection invalidate]; // Don't need to set _activeConnection to nil, we don't expect new connections [_xpcListener invalidate]; _xpcListener = nil; } - (BOOL)listener:(NSXPCListener *)__unused listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { if (_activeConnection != nil) { SULog(SULogLevelError, @"Error: Rejecting new connection for agent due already having an active connection"); [newConnection invalidate]; return NO; } // Hardening but not critical for security NSError *validationError = nil; SUValidateConnectionStatus validationStatus = [SUCodeSigningVerifier validateConnection:newConnection options:SUValidateConnectionOptionDefault error:&validationError]; switch (validationStatus) { case SUValidateConnectionStatusSetCodeSigningRequirementSuccess: break; case SUValidateConnectionStatusSetNoRequirementSuccess: break; case SUValidateConnectionStatusAPIFailure: case SUValidateConnectionStatusCodeSigningRequirementFailure: case SUValidateConectionNoSupportedValidationMethodFailure: SULog(SULogLevelError, @"Error: Rejecting new connection for agent due to failing validation of XPC connection with status %lu and error: %@", validationStatus, validationError.localizedDescription); [newConnection invalidate]; return NO; } newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SUInstallerAgentInitiationProtocol)]; newConnection.exportedObject = self; newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SPUInstallerAgentProtocol)]; _activeConnection = newConnection; __weak __typeof__(self) weakSelf = self; newConnection.interruptionHandler = ^{ dispatch_async(dispatch_get_main_queue(), ^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf->_activeConnection invalidate]; } }); }; newConnection.invalidationHandler = ^{ dispatch_async(dispatch_get_main_queue(), ^{ __typeof__(self) strongSelf = weakSelf; if (strongSelf != nil) { [strongSelf->_delegate agentConnectionDidInvalidate]; } }); }; _agent = newConnection.remoteObjectProxy; [newConnection resume]; return YES; } - (void)connectionDidInitiateWithReply:(void (^)(void))acknowledgement { dispatch_async(dispatch_get_main_queue(), ^{ self->_connected = YES; [self->_delegate agentConnectionDidInitiate]; }); if (acknowledgement != NULL) { acknowledgement(); } } - (void)connectionWillInvalidateWithError:(NSError *)error { dispatch_async(dispatch_get_main_queue(), ^{ self->_invalidationError = error; }); } @end