// // XPCConnectionHandler.swift // AltDaemon // // Created by Riley Testut on 9/14/20. // Copyright © 2020 Riley Testut. All rights reserved. // import Foundation import Security class XPCConnectionHandler: NSObject, ConnectionHandler { var connectionHandler: ((Connection) -> Void)? var disconnectionHandler: ((Connection) -> Void)? private let dispatchQueue = DispatchQueue(label: "io.altstore.XPCConnectionListener", qos: .utility) private let listeners = XPCConnection.machServiceNames.map { NSXPCListener.makeListener(machServiceName: $0) } deinit { self.stopListening() } func startListening() { for listener in self.listeners { listener.delegate = self listener.resume() } } func stopListening() { self.listeners.forEach { $0.suspend() } } } private extension XPCConnectionHandler { func disconnect(_ connection: Connection) { connection.disconnect() self.disconnectionHandler?(connection) } } extension XPCConnectionHandler: NSXPCListenerDelegate { func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { let maximumPathLength = 4 * UInt32(MAXPATHLEN) let pathBuffer = UnsafeMutablePointer.allocate(capacity: Int(maximumPathLength)) defer { pathBuffer.deallocate() } proc_pidpath(newConnection.processIdentifier, pathBuffer, maximumPathLength) let path = String(cString: pathBuffer) let fileURL = URL(fileURLWithPath: path) var code: UnsafeMutableRawPointer? defer { code.map { Unmanaged.fromOpaque($0).release() } } var status = SecStaticCodeCreateWithPath(fileURL as CFURL, 0, &code) guard status == 0 else { return false } var signingInfo: CFDictionary? defer { signingInfo.map { Unmanaged.passUnretained($0).release() } } status = SecCodeCopySigningInformation(code, kSecCSInternalInformation | kSecCSSigningInformation, &signingInfo) guard status == 0 else { return false } // Only accept connections from AltStore. guard let codeSigningInfo = signingInfo as? [String: Any], let bundleIdentifier = codeSigningInfo["identifier"] as? String, bundleIdentifier.contains(Bundle.Info.appbundleIdentifier) else { return false } let connection = XPCConnection(newConnection) newConnection.invalidationHandler = { [weak self, weak connection] in guard let self = self, let connection = connection else { return } self.disconnect(connection) } self.connectionHandler?(connection) return true } }