deployment/macos/diagnostic/SystemExtensionTester/TestSystemExtension/IPCConnection.swift (99 lines of code) (raw):

/* See LICENSE folder for this project's licensing information. Abstract: This file contains the implementation of the app <-> provider IPC connection */ import Foundation import os.log import Network import EndpointSecurity /// App --> Provider IPC @objc protocol ProviderCommunication { func register(_ completionHandler: @escaping (Bool) -> Void) func attemptFullDiskAccess(_ completionHandler: @escaping (Bool) -> Void) } /// The IPCConnection class is used by both the app and the system extension to communicate with each other class IPCConnection: NSObject { // MARK: Properties var listener: NSXPCListener? var currentConnection: NSXPCConnection? static let shared = IPCConnection() // MARK: Methods /** The NetworkExtension framework registers a Mach service with the name in the system extension's NEMachServiceName Info.plist key. The Mach service name must be prefixed with one of the app groups in the system extension's com.apple.security.application-groups entitlement. Any process in the same app group can use the Mach service to communicate with the system extension. */ private func extensionMachServiceName(from bundle: Bundle) -> String { guard let networkExtensionKeys = bundle.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any], let machServiceName = networkExtensionKeys["NEMachServiceName"] as? String else { fatalError("Mach service name is missing from the Info.plist") } return machServiceName } func startListener() { let machServiceName = extensionMachServiceName(from: Bundle.main) os_log("Starting XPC listener for mach service %@", machServiceName) let newListener = NSXPCListener(machServiceName: machServiceName) newListener.delegate = self newListener.resume() listener = newListener } /// This method is called by the app to register with the provider running in the system extension. func register(withExtension bundle: Bundle, completionHandler: @escaping (Bool) -> Void) { guard currentConnection == nil else { os_log("Already registered with the provider") completionHandler(true) return } let machServiceName = extensionMachServiceName(from: bundle) let newConnection = NSXPCConnection(machServiceName: machServiceName, options: []) // The remote object is the provider's IPCConnection instance. newConnection.remoteObjectInterface = NSXPCInterface(with: ProviderCommunication.self) currentConnection = newConnection newConnection.resume() guard let providerProxy = newConnection.remoteObjectProxyWithErrorHandler({ registerError in os_log("Failed to register with the provider: %@", registerError.localizedDescription) self.currentConnection?.invalidate() self.currentConnection = nil completionHandler(false) }) as? ProviderCommunication else { fatalError("Failed to create a remote object proxy for the provider") } providerProxy.register(completionHandler) } func queryFullDiskAccessFromSystemExtension(completionHandler: @escaping (Bool) -> Void) { // Guard nil connection guard self.currentConnection != nil else { completionHandler(false) return } guard let providerProxy = self.currentConnection?.remoteObjectProxyWithErrorHandler({ error in os_log("Unable to communicate with system extension: %@", error.localizedDescription) self.currentConnection?.invalidate() self.currentConnection = nil completionHandler(false) }) as? ProviderCommunication else { os_log("Unable to communicate with system extension") completionHandler(false) return } providerProxy.attemptFullDiskAccess(completionHandler) } } extension IPCConnection: NSXPCListenerDelegate { // MARK: NSXPCListenerDelegate func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { // The exported object is this IPCConnection instance. newConnection.exportedInterface = NSXPCInterface(with: ProviderCommunication.self) newConnection.exportedObject = self newConnection.invalidationHandler = { self.currentConnection = nil } newConnection.interruptionHandler = { self.currentConnection = nil } currentConnection = newConnection newConnection.resume() return true } } extension IPCConnection: ProviderCommunication { // MARK: ProviderCommunication func register(_ completionHandler: @escaping (Bool) -> Void) { os_log("App registered") completionHandler(true) } func attemptFullDiskAccess(_ completionHandler: @escaping (Bool) -> Void) { var client: OpaquePointer? guard (es_new_client(&client) { (client, message) in os_log("ES Message received") }) == ES_NEW_CLIENT_RESULT_SUCCESS else { completionHandler(false) return } es_delete_client(client!) completionHandler(true) } }