Post

Replies

Boosts

Views

Activity

macOS: NEDNSProxyProvider and NEFilterDataProvider = UDP datagram truncation
Environment: macOS 13.5 and 13.4 Cisco VPN installed with active DNS Proxy, Content Filter and Transparent Proxy extensions (yes I know Apple can't support 3rd parties here). Our Content Filter extension loaded. In this scenario DNS packets are being truncated after our extension asks to see more data from the Cisco DNS Proxy flow. Specifically we ask to see a max of 96 bytes for all UDP traffic. When a DNS query is made the DNS response is truncated to 96 bytes after we return [allowVerdict] from our [handleInboundDataFromFlow:] for that first 96 bytes. This only occurs when both our extension and the Cisco extensions are loaded. It does not occur if just our extension is loaded or just the Cisco extensions are loaded. So my question is are we doing something wrong by returning [allowVerdict] after the initial 96 byte inspection in [handleInboundDataFromFlow:]? Should we instead try to only respond after a complete datagram is received? But how to do that when we do not know the datagram size because we only request peek data for TCP/UDP (thus no IP header info)? Is Cisco at fault? From inspecting their binary it appears they are not using the NWUDPSession/NWTCPConnection classes as recommend by Apple, but are instead using their own custom classes that probably wrap the BSD socket API. Is this a OS bug? From what I know NEFilterFlow is a stream and the OS is supposed to handle proper data reassembly. But somehow a truncated UDP datagram is being passed on to the Cisco filter (and dig). Shouldn't the system send the complete datagram after we return [allowVerdict] for the initial 96 bytes?
4
0
598
Aug ’23
macOS NEFilterDataProvider best practices?
I've seen some discussion around performance on the forums but nothing official. What is the best practice for [handleNewFlow:] , [handleOutboundDataFromFlow:], and [handleInboundDataFromFlow:] callbacks in a content filter? Are all flows funneled through a single serial queue that calls into my subclass? If so this seems like we are back in the days of early OS X with the kernel network funnel serializing all network traffic. Should I offload flow processing onto a concurrent queue and then pause the flow and return from my callback? Or just do all processing in the callbacks? And once I return an allow/deny verdict for the flow (without asking for more data) do I no longer see that flow's traffic in my content filter? That's what I would expect and it seems to be the case in actual practice too. For reference I never need to interact with the user. All of the rules are loaded from an EDR platform. I bring this up because we have users complaining of "stuttering" during Google Meet / Zoom, etc when our extension is active and from our perf metrics time spent in the callbacks are minimal (a few hundred usecs). But if all traffic is serialized through our content filter and the system is very busy I wonder if this could lead to dropped packets. What if multiple content filters are present? Are they all serialized with each other? Oof.
4
0
713
Aug ’23
macOS: Only one DNS Proxy NSYSEXT allowed?
A client is seeing this behavior in their fleet. They have a 3rd party DNS Proxy and when that is loaded our DNS Proxy is immediately sent a stop message. I saw this behavior mentioned in another thread, but I've not been able to find an official stance on this from Apple. So is this expected behavior? This was observed on macOS 12.4 and 12.3 for both Apple Silicon and Intel. Thanks. p.s. I can understand the restriction, but our client is requesting further information. As they would like both proxies running.
4
0
819
Jul ’22
No EndpointSecurity symlink event?
I'm watching NOTIFY_LINK and NOTIFY_CREATE events and symlinks are oddly not reported as link events but as create events. The problem with this is that I cannot get the path linked from without doing a manual readlink. Am I missing something? Is this an intentional design decision in ES? With BSM we'd watch AUE_SYMLINK (which has its own issues with not reporting paths).
3
0
675
Jul ’21
Enabling DNSProxy configuration fails with permission denied
M1, macOS 11.1, SIP disabled, AMFI enabled: I'm trying to bring up a DeveloperID NEDNSProxyProvider but it's failing to install the configuration: default 13:30:51.215874-0600 Kringle No configurations exist default 13:30:51.221481-0600 Kringle Saving configuration Filter DNS requests. with existing signature (null) default 13:30:51.221569-0600 Kringle Adding F471D6C9-9794-46F3-8E57-91253EC91292 to the loaded configurations default 13:30:51.223047-0600 Kringle Clearing F471D6C9-9794-46F3-8E57-91253EC91292 from the loaded configurations error 13:30:51.223121-0600 Kringle Failed to save configuration Filter DNS requests.: Error Domain=NEConfigurationErrorDomain Code=10 "permission denied" UserInfo={NSLocalizedDescription=permission denied} error 13:30:51.223181-0600 Kringle -[NEDNSProxyManager saveToPreferencesWithCompletionHandler:]_block_invoke_3: failed to save the new configuration: Error Domain=NEConfigurationErrorDomain Code=10 "permission denied" UserInfo={NSLocalizedDescription=permission denied} error 13:30:51.222955-0600 nehelper Kringle Failed to obtain authorization right for 3: no authorization provided Everything is code signed, I have the correct entitlements and the SYSEXT Is being loaded. It's this configure step that is failing. It fails with the same error as root. The app is not notarized (but SIP is disabled to bypass this). Is this the reason? * 5JVV5R9542 org.bergstrand.kringle.daemon (1.0/1210.46.18) org.bergstrand.kringle.daemon[activated enabled] Here's the configuration load code: #define KRINGLE_DAEMON_ID "org.bergstrand.kringle.daemon" void configureDNSProxyExtension(dispatch_block_t callback) {   NEDNSProxyManager* mgr = NEDNSProxyManager.sharedManager;   [mgr loadFromPreferencesWithCompletionHandler:^(NSError* error) {     if (error) {       LOGE(@"Failed to load cfg: %@", error);       callback();       return;     }          if (const BOOL enable = SNTConfigurator.configurator.enableDNSProxyExtension; enable != mgr.enabled) {       // making a change -- callback on save completion       if (enable) {         mgr.localizedDescription = @"Filter DNS requests.";         NEDNSProxyProviderProtocol* proto = [[NEDNSProxyProviderProtocol alloc] init];         proto.providerBundleIdentifier = @(KRINGLE_DAEMON_ID);         mgr.providerProtocol = proto;         mgr.enabled = YES;       } else {         mgr.enabled = NO;       }       [mgr saveToPreferencesWithCompletionHandler:^(NSError* error) {         if (!error) {           if (enable) {             LOGD_MSG("Filter enabled.");           } else {             LOGD_MSG("Filter disabled.");           }         } else {           LOGE(@"Failed to save %@ cfg: %@", enable ? @"enabled" : @"disabled", error);         }         callback();       }];     } else {       callback(); // no change made     }   }]; } (void)request:(OSSystemExtensionRequest *)request     didFinishWithResult:(OSSystemExtensionRequestResult)result {   NSLog(@"SystemExtension \"%@\" request did finish: %ld", request.identifier, (long)result);      configureDNSProxyExtension(^{     exit(0);   }); } req = [OSSystemExtensionRequest activationRequestForExtension:e queue:dispatch_get_main_queue()];
1
0
826
Feb ’21