Post

Replies

Boosts

Views

Activity

Reply to tccd reports Apple Events entitlement check error, despite a process having it
Sorry, writing a response during the night in a foreign language was a bad idea... I mean, I have a "container" bundle and a "nested" bundle which I put in "container".app/Contents/MacOS. The container application launches the nested one as an agent. I tested launching via launchctl and Service Management framework. It turns out that I have to put "Apple Events" entitlements for both the container and the nested app and update their Info.plists accordingly (put there Privacy - AppleEvents Sending Usage Description), even though only the nested app deals with Scripting Bridge. When a nested app reaches the execution point where it invokes Scripting Bridge API a corresponding popup from the system appears to get the user verdict (allow/deny access). However, the message in this popup states that Automation permission is requested by a container app that doesn't deal with Apple Events and acts as a simple launcher for the nested one. It turns out that I didn't need AssociatedBundleIdentifiers in the nested app's Info.plist (so far, I don't know whether it could a problem for notarization).
Jun ’24
Reply to tccd reports Apple Events entitlement check error, despite a process having it
@DTS Engineer So, here is what I have investigated. If I put the application bundle inside another bundle I should set entitlements and Info.plist accordingly for both container and nested app. In that case despite the way you are launching a nested application (either via launchctl utility or via Service Management framework). With this setup, I could finally see the message about the process asking for Automation permission. However, the application name in the message wasn't the container's not the nested app's.
Jun ’24
Reply to tccd reports Apple Events entitlement check error, despite a process having it
Probably you are right, I am going to check with your suggestions, thank you! So far I have modified the UI element app so I can run it as a standalone GUI-less app (via Xcode without launchctl invocation or by double-clicking on the bundle). By GUI-less I mean with "Application is agent" key set to YES in Info.plist and without "Main storyboard" key. In this configuration, the app can acquire Automation permissions and interact with Finder via Scripting Bridge.
May ’24
Reply to tccd reports Apple Events entitlement check error, despite a process having it
Hi, eskimo! Finally, I was able to check that. This termination mystery was solved: an exit() statement caused app termination. The statement was in the program itself and it had nothing in common with tccd. However, the issue with tccd that caused the creation of this thread persists. It continues to report the absence of entitlements for Apple Events despite the fact, that the bundle has it. If we return to our naming convention: "UI element app" is a user agent that has the corresponding plist and gets launched via launchctl command from "container app". UI element app bundle is a part of the container app bundle. Considering these conditions is it possible that the issue may be with the container app not having the required entitlements?
May ’24
Reply to tccd reports Apple Events entitlement check error, despite a process having it
Sorry, responded in the comment section. Duplicate as a reply: Ok, considering only the real product: The UI element app is an app, right? Not something else, like an app extension. Exactly, that is an app not an app extension or anything else Are either of these sandboxed? They aren't sandboxed Where did you app the Apple event capability? On the UI element app? Yes, on the UI element app via Xcode "Signing & Capabilities" In that TCC error, which bundle ID and path are shown? The container app? Or the UI element app? UI element app
May ’24
Reply to How to redirect TCP flow using Transparent Proxy to original destination
I have read Handling flow copying article. So, it doesn't clear to me how I can switch between reading and writing on both sides of the connection. Here is the example of outboundCopier() from the article let flow: NEAppProxyTCPFlow let connection: NWConnection // Reads from the flow and writes to the remote connection. func outboundCopier() { flow.readData { (data, error) in if error == nil, let readData = data, !readData.isEmpty { connection.send(content: readData, completion: .contentProcessed( { connectionError in // Handle completion success or error. // Set up another read if there is no error. if connectionError == nil { self.outboundCopier() } })) } else { // Handle error case or the read that contains empty data. } } } I see that the method is called recursively until there is data that can be sent from the flow to the remote endpoint. However, I don't get where we should call inboundCopier() to read the response. Should it be called in the else branch from the outboundCopier(), when we detect that there is no flow data left unread? Or I can call inbound and outbound copiers synchronously inside the flow open() completion handler, when the state of the NWConnection transfers to .ready? For instance: case .ready:   flow.open(withLocalEndpoint: nil) { error in     if let error = error {     os_log(.debug, log: self.log, "TCP flow opening failed %@", error.localizedDescription)         return     } self.outboundCopier()     self.inboundCopier() } Let's assume that inboundCopier() and outboundCopier() have the same implementations as in the article Thank you in advance, for explanation
Aug ’22
Reply to How to redirect TCP flow using Transparent Proxy to original destination
Thank you for reply, Eskimo! So, I try to handle TCP flow like this override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {     os_log(.debug, log: self.log, "Handle new flow")         if let tcpFlow = flow as? NEAppProxyTCPFlow {         guard let targetEndpoint = tcpFlow.remoteEndpoint as? NWHostEndpoint else {             os_log(.debug, log: self.log, "On outboundCopierTCP. Unable to cast remote endpoint to NWHostEndpoint")                 return false             }             let tcpConnectionManager = TCPConnectionManager(flow: tcpFlow, endpoint: targetEndpoint)             tcpConnectionManager.startExchangingData() return true         }         return false     } Am I heading to the right direction? You can review a TCPConnectionManagerClass implementation in my reply above. It manages NWConnection with intended target of the flow and read\write operation between connection and flow
Aug ’22
Reply to How to redirect TCP flow using Transparent Proxy to original destination
Finally, TCPConnectionManager class TCPConnectionManager {     public let flow: NEAppProxyTCPFlow     private let connection: NWConnection     private let tcpConnection = NWTCPConnection()     private let log = OSLog(subsystem: "com.ybelikov.transparent.proxy.network.extension", category: "provider")     private let connectionQueue = DispatchQueue(label: "com.ybelikov.transparent.proxy.network.extension.TCPQueue")     init(flow: NEAppProxyTCPFlow, endpoint: NWHostEndpoint) {         self.flow = flow         let host = Network.NWEndpoint.Host(endpoint.hostname)         let port = Network.NWEndpoint.Port(endpoint.port) ?? Network.NWEndpoint.Port.any         os_log(.debug, log: self.log, "On TCP init. host: %@, port: %@", String(describing: host), String(describing: port))         self.connection = NWConnection(host: host, port: port, using: .tcp)     }        public func startExchangingData() {     connection.stateUpdateHandler = self.stateChangedCallback(to:)         connection.start(queue: connectionQueue)     }     private func stateChangedCallback(to state: NWConnection.State) {             switch state {             case .ready:                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is ready")                 flow.open(withLocalEndpoint: tcpConnection.localAddress as? NWHostEndpoint) { error in                     if let error = error {                         os_log(.debug, log: self.log, "TCP flow opening failed %@", error.localizedDescription)                         return                     }                     self.handleOutgoingTCPData()                 }                 break             case .failed(let error):                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is failed %@", error.localizedDescription)                 self.connection.cancel()                 break             case .cancelled:                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is cancelled")                 break             case .preparing:                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is preparing")                 break             case .setup:                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is in setup state")                 break             case .waiting(let error):                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. TCP connection is in waiting state, %@", error.localizedDescription)                 self.connection.cancel()                 break                     default:                 os_log(.debug, log: self.log, "TCPConnectionManager::stateChangedCallback. State is unknown")                 break             }     }          private func handleOutgoingTCPData() {         os_log(.debug, log: self.log, "handleOutgoingTCPData. handling TCP flow data")         flow.readData { data, error in             if let error = error {                 os_log(.debug, log: self.log, "handleOutgoingTCPData. Error on handling TCP flow data: %@", error.localizedDescription)                 return             }             if let data = data, !data.isEmpty {                 self.connection.send(content: data, completion: .contentProcessed({ error in                     if let error = error {                         os_log(.debug, log: self.log, "TCPConnectionManager::sendDataToEndpoint. TCP error: %@", error.localizedDescription)                         return                     }                     os_log(.debug, log: self.log, "TCP data sent successfully")                     self.handleOutgoingTCPData()                 }))             } else {                 self.handleIncomingTCPData()             }     }     }     private func handleIncomingTCPData() {         os_log(.debug, log: self.log, "On handleIncomingTCPData")         connection.receive(minimumIncompleteLength: 0,                                maximumLength: 2048) { (data, _, isComplete, error) in             switch (data, isComplete, error) {             case (let data?, _, _):                 os_log(.debug, log: self.log, "On handleIncomingTCPData. Received data, writing it to the flow")                 self.flow.write(data) { writeError in                     if writeError == nil {                         os_log(.debug, log: self.log, "On handleIncomingTCPData. Write error is nil")                         self.handleIncomingTCPData()                     }                 }             case (_, true, _):                 os_log(.debug, log: self.log, "On handleIncomingTCPData. Connection is completed")                 self.connection.stateUpdateHandler = nil                 self.connection.cancel()                 self.flow.closeReadWithError(error)                 self.flow.closeWriteWithError(error)             case (_, _, let error?):                 os_log(.debug, log: self.log, "On handleIncomingTCPData. Error: %@", error.localizedDescription)                 self.connection.cancel()                 self.flow.closeReadWithError(error)                 self.flow.closeWriteWithError(error)             default:                 os_log(.debug, log: self.log, "On handleIncomingTCPData. Unknown case")             }         }     } }
Aug ’22
Reply to Multiple network extension in a pipeline
Hi, Matt and Oskar! Can you explain how the traffic can be redirected from one extension to another? For instance, I would like to set up a pipeline with NETransparentProxyProvider and NEPacketTunnelProvider. As I understand from Matt's reply, if I process traffic inside the transparent proxy and redirect it somewhere, the tunnel provider will consider this traffic as originating from the proxy instead original application. However, for me, it is not clear how the proxy would redirect traffic to the packet tunnel. Should the extension with NEPacketTunnelProvider runs a sort of a server to sniff all traffic from the proxy?
Aug ’22
Reply to NETrasparentProxy::startProxy never gets called
So, I have managed to see the desired log about proxy start. However I don't see logs inside handleNewFlow. The method contains nothing complicated. All that I want to achieve for now is to "see" all outgoing traffic and allow it. override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {         os_log(.debug, log: self.log, "Handle new flow")         return false } inside startProxy I configured NETransparentProxySettings like this, to sniff all outgoing traffic: override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {     os_log(.debug, log: self.log, "Proxy started")         let proxyNetworkSettings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1")         let allOutgoingTCPTrafficRule = NENetworkRule(remoteNetwork: nil, remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol: .TCP, direction: .outbound)         let allOutgoingUDPTrafficRule = NENetworkRule(remoteNetwork: nil, remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol: .UDP, direction: .outbound)         proxyNetworkSettings.includedNetworkRules = [allOutgoingTCPTrafficRule, allOutgoingUDPTrafficRule]         setTunnelNetworkSettings(proxyNetworkSettings) { error in             if let error = error {                 os_log(.debug, log: self.log, "Error on saving tunnel network settings")                 return             }             os_log(.debug, log: self.log, "Tunnel network settings saved correctly")         }  } Also, I see in the Network Settings panel that the proxy's network profile hangs in "connecting" state forever. Is there a problem with tunnelRemoteAddress? What I have to configure to be able to track outgoing flows?
Aug ’22