Files

142 lines
4.3 KiB
Objective-C

//
// 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 () <NSXPCListenerDelegate, SUInstallerAgentInitiationProtocol>
@end
@implementation AgentConnection
{
NSXPCListener *_xpcListener;
NSXPCConnection *_activeConnection;
__weak id<AgentConnectionDelegate> _delegate;
}
@synthesize agent = _agent;
@synthesize connected = _connected;
@synthesize invalidationError = _invalidationError;
- (instancetype)initWithHostBundleIdentifier:(NSString *)bundleIdentifier delegate:(id<AgentConnectionDelegate>)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 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