I've implemented a custom VPN for macOS (system extension, Packet Tunnel Provider).
I've configured disconnectOnSleep = false, and at the Provider I've implemented the sleep() and wake() functions.
At the wake() func, I'm trying to re-establish the connection, and most of the time it's working well.
However, there are times when even after wake() is called, it seems that the interfaces aren't ready/available, and I'm getting "Network is unreachable" errors (I'm working with BSD Sockets).
Any idea why the interfaces aren't available at this point, after wake() had been called?
Any idea on how to be updated when the interfaces are available?
We are working on a Network Extension based iOS app.
Is it possible to have an app proxy (for per-app VPN) and packet tunnel providers within the same extension binary on iOS? On Mac this seems to be allowed, but with iOS it looks like we have to separate binaries - one per provider. In our case this complicates things.
Hi
We are building an macOS application which integrates VPN functions right now. We are using developer ID ceritifcate to sign the app and system network extension and sandbox is enabled.
One issue we are facing now is that we need to establish mTLS connection to server. During this connection, we need to send client certificate to server via provideIdentity() API.
We have the certificate, key and p12 file which are generated in another daemon. But we can not use SecPkcs12Import function to import the p12 file in our system extension due to the sandbox limitation and the different context.
I know that we cannot construct secIdentity object by ourselves. So I am wondering if there is any way that we can get the secIdentity object in system extension?
Is it possible to send secIdentity object between app and system extension?
Post not yet marked as solved
I built an app which hosts a CMIOExtension. The app works, and it can activate the extension. The extension loads in e.g. Photo Booth and shows the expected video (a white horizontal line which moves down the picture).
I have a couple of questions about this though.
The sample Camera Extension is built with a CMIOExtension dictionary with just one entry, CMIOExtensionMachServiceName which is $(TeamIdentifierPrefix)$(PRODUCT_BUNDLE_IDENTIFIER)
This Mach service name won't work though. When attempting to activate the extension, sysextd says that the extensions has an invalid mach service name or is not signed, the value must be prefixed with one of the App Groups in the entitlement.
So in order to get the sample extension to activate from my app, I have to change its CMIOExtensionMachServiceName to
<my team ID>.com.mycompany.my-app-group.<myextensionname>
Is this to be expected?
The template CMIOExtension generates its own video using a timer. My app is intended to capture video from a source, filter that video, then feed it to the CMIOExtension, somehow. The template creates an app group called "$(TeamIdentifierPrefix)com.example.app-group", which suggests that it might be possible to use XPC to send frames from the app to the extension.
However, I've been unable to do so. I've used
NSXPCConnection * connection = [[NSXPCConnection alloc] initWithMachServiceName:, using the CMIOExtensionMachServiceName with no options and with the NSXPCConnectionPrivileged option. I've tried NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: using the extension's bundle identifier. In all cases when I send the first message I get an error in the remote object proxy's handler:
Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named <whatever name I try> was invalidated: failed at lookup with error 3 - No such process."
According to the "Daemons and Services Programming Guide" an XPC service should have a CFBundlePackageType of XPC!, but a CMIOExtension is of type SYSX. It can't be both.
Does the CMIOExtension loading apparatus cook up a synthetic name for the XPC service, and if so, what is it? If none, how is one expected to get pixel buffers into the camera extension?
Post not yet marked as solved
I have a requirement where I need to monitor writes/reads from arbitrary USB devices. Can I do it with USBDriverKit?
I added a Camera Extension to my app, using the template in Xcode 13.3.1. codesign tells me that the app and its embedded system extension are correctly signed, their entitlements seem to be okay. But when I submit an activation request for the extension, it returns with this failure:
error: Error Domain=OSSystemExtensionErrorDomain Code=9 "(null)"
localized failure reason: (null)
localizedDescription: The operation couldn’t be completed. (OSSystemExtensionErrorDomain error 9.)
localizedRecoverySuggestion: (null)
What could be the reason? code 9 appears to mean a "validation error", but how do I figure out what is invalid?
In reference to this related question: forum question 678260
I have an application that is codesigned and notarized to install a VPN extension using the NextworkExtension plugin. It works great in Xcode in debug.
In release builds that are notarized the network extension is rejected when I try to load it. The only way we were able to get the extension to load is by going through the system extension API.
**Quinn, is it possible to distribute Developer ID-signed apps that install NetworkExtension components outside the App Store without having to use System Extension?
**
The 4 UIs that the user has to jump through to allow System Extensions is going to be a huge problem for non-technical user base.
CONSOLE output when installed from a notarized pkg:
NEVPNTunnelPlugin(com.foo.bar[inactive]): Validation of the extension failed
and
Provider com.foo.bar validation failed: Error Domain=NEFilterErrorDomain Code=1 "(null)"
Hi.
Our NWListener runs fine and accepts connections successfully when it's run in a standalone app, however, the same code fails when moved into the System Extension code.
Specifically, we get the error: The operation couldn’t be completed. (Network.NWError error 0.)
...
let listener = try NWListener(using: params!)
listener.service = NWListener.Service(name: "service",
type: "_service._tcp")
listener.stateUpdateHandler = { newState in
switch newState {
case .ready:
if let port = listener.port {
self.receiveIncomingDataOnConnection()
}
case .failed(let error):
listener.cancel()
print("Listener - failed with %{public}@, restarting", error.localizedDescription)
// Getting the error ^
default:
break
}
}
...
We have checked that the App Sandbox permissions for inbound and outbound connections are set in the entitlements file.
At this point, we are stumped at what's limiting our listener when it's running in the extension.
Thanks!
Post not yet marked as solved
I've implemented a custom VPN system extension for macOS (Packet Tunnel Provider).
I created a tunnel, and I have a VPN connection, with the default (IPv4) routes.
My question is about sending traffic which was originated at the extension, via the tunnel.
Is it possible to create a BSD socket at the extension, and bind it to a specific interface, so the traffic (that was created from the extension) for this socket will be routed via the tunnel?
Post not yet marked as solved
I have an app which is installing a system extension. On startup I want to know if it's already been installed or not (because if it hasn't I want to show some information to the user).
I have not been able to find an ObjC or Swift method that allows me to ask if a given extension has been installed.
I know I can run systemextensionctl list and parse the output but I don't want to have to fork a shell command if I don't have to.
Post not yet marked as solved
We have a macOS app that contains a system extension content filter as part of the app bundle. The main container app is a relatively simple process to perform activation and deactivation of the content filter.
From guidance given on this forum, our container app has a GUI component (AppDelegate) which on launch activates the content filter if needed, e.g. on initial install or update. This works as intended, provided the user is logged in.
However, we would normally expect the install/update/removal to be performed by remote management, e.g. pushed by JAMF, which often happen when no user was logged in on the device. Note, we have a MDM profile which provides pre-authorization of the system extension and content filter to negate the requirement for the user to respond to prompts during install.
Trying to perform a remote install or removal, requires calling the main container app to run without a logged in user which fails because the app terminates as there is no GUI context to run in.
Trying a container app without a GUI component appears to be unreliable and often hangs during content filter activation.
What is the correct way to perform installation or removal, without a user login, via remote management?
Post not yet marked as solved
I've implemented a custom system-extension VPN (Packet Tunnel Provider) for macOS.
At the extension, I need to use a 3rd party dynamic lib.
The steps I did:
Build phases:
Copy files, with Frameworks destination
Link Binary With Libraries
Build Settings:
I set 'Dynamic Library Install Name', 'Dynamic Library Install Name Base', and 'Library Search Path' to the lib folder
I set 'Header Search Path' to the headers folder
But when running the extension, it's crashing with the error
Termination Reason: Namespace DYLD, Code 1 Library missing
Library not loaded: @loader_path/somelib.dylib
And
Reason: tried: '/Library/SystemExtensions/A1111-someID-11111/com.myapp.myappSysExtension.systemextension/Contents/MacOS/libwavmodapi.dylib' (no such file), '/usr/local/lib/libwavmodapi.dylib' (no such file), '/usr/lib/libwavmodapi.dylib' (no such file)
(terminated at launch; ignore backtrace)
Any idea what I'm doing wrong here?
Also, is it even possible to use dynamic libs from a sys-ext?
When network extension is installed, the tethering breaks.
So our customers faced a very big problem.
Give me some feedback.
The test steps are as follows.
1.Test Info.
os : higher than 12.1
hardware : only MacBookPro18, 1~3
and, only Tethering(IPv6).
It does not happen in IPv4.
It works normally in a different environment.
2.Test Step
1)We always allowed Ne's authority request in the same way.
2)After NE is installed, press Allow network filtering.
3)Our NE calls the API below(in main method)
[NEFilterPacketProvider startSystemExtensionMode];
4)However, the following error message occurs.
error 16:10:37.791557+0900 com. ahnlab. TobeyNE [self.extensionContext conformsToProtocol:auxHostProtocol.protocol] - /AppleInternal/Library/BuildRoots/66382bca-8bca-11ec-aade-6613bcf0e2ee/Library/Caches/com.apple.xbs/Sources/ExtensionFoundation/ExtensionFoundation/Source/NSExtension/NSExtensionSupport/EXExtensionContext.m:332: Class NEFilterPacketExtensionProviderContext does not conform to aux host protocol: <private>
5)and, the tethering breaks.
Also, it works normally unless it is MacBookPro18, 1.
There is this issue only in MacBookPro18, 1.
Tell me the solution.
Post not yet marked as solved
I manage a system that automatically generates system extensions, and in order to ensure that every release's dext can be activated over top of any other release, I have it increment the dext's CFBundleVersion so that every release has a unique version number.
However I just hit an error where, once CFBundleVersion reached 10000, dext activation would fail with this error:
Property "CFBundleVersion" must be a valid kext version
Apparently the maximum value any one of the three digits can be for CFBundleVersion is 9999, at least for a dext. So if CFBundleVersion only uses one number as opposed to three, it can't go higher than 9999.
Note that I found this out from experimentation. No where in Apple's documentation have I found any mention of this restriction, including, most distressingly, in the docs for CFBundleVersion.
Since I'm programmatically assigning this value, now I need to know: Are there any other requirements or restrictions for CFBundleVersion?
I'm curious if there are any in the context of an app (be it on macOS, iOS, tvOS, and so on) or other types of extensions, as well as with a DriverKit extension.
Post not yet marked as solved
Hello,
Captive portal screen is not appearing when we are connecting to wifi for the first time. This is happening only when System Extension is enabled. We are using NETransparentProxyProvider System Extension. These are the details-
OS version- Big sur 11.6.3
System Extension- NETransparentProxyProvider
Internet provider- ACT internet
Ruleset-
NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "0.0.0.0", port: "80"), remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol:.TCP, direction: .outbound),
NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "0.0.0.0", port: "443"), remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol:.TCP, direction: .outbound),
NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "0.0.0.0", port: "80"), remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol:.UDP, direction: .outbound),
NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "0.0.0.0", port: "443"), remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol:.UDP, direction: .outbound)
Post not yet marked as solved
If I install an app that includes a system extension I've noticed that when I remove this app the system extension getting uninstall.
As part of our feature(Tamper protection) while we drag the container app to the trash. we shouldn't allow this app deletion and system extension Should be activate and Enabled.
Here we are able to Restrict the app deletion successfully but system extension is getting inactive and terminated.
So is there any way to avoid/handle uninstallation of system extension while drag the container app to the trash.
Post not yet marked as solved
I mean, I could do a readdir on /Library/SystemExtensions/, but that won't really tell me which ones are active. I could parse the output of systemextensionsctl list but that doesn't seem particularly good.
In recent versions of macOS Big Sur and Monterey, SystemExtnesions are getting replaced with same version.
We have an application with a system extension and UI in the app displays status of the system extension to remind users to approve it in case they have not. Since there is no way to query the status of a SystemExtnesion, to display the status in UI, we would submit the SystemExtension request every time UI is displayed and update the status as per the delegate callback. Earlier, if SystemExtension is already approved, with would immediately get .completed result.
However, since recently we are noticing that whenever the app submits the extension request, if there is already an approved SystemExtension, then replacement delegate callback is triggered for same extension version. If we return .replace action, the existing SystemExtension is getting disabled and replaced with SystemExtension of same version. This is contradictory to the documentation of replacement delegate callback which indicates that replacement request will only be called when an extension of different version is found.
From the documentation:
The manager calls this method when it encounters an existing extension with the same team and bundle identifiers, but with different version identifiers. It uses the CFBundleVersion and CFBundleShortVersionString identifiers to determine if the existing and new versions differ. The delegate must make a decision on whether to replace the existing extension.
This issue is noticed in development as well as production signed version of our application and in the same run of the same application process.
This unexpected behaviour is causing issues in our app. For example, when the existing SystemExtension is being disabled, all our network extensions are being stopped. This is resulting in loss of functionality.
Here are some relevant console logs, attached.
logs.txt
In another question on this forum (https://developer.apple.com/forums/thread/124775) eskimo stated that launching a system extension from an daemon is not the right approach and that the OSSystemExtensionRequest.activationRequest API should be called from an App.
My question is, does this same restriction apply to a LaunchAgent started App?
If so, to ensure activation as soon as possible is the only option to use a SMLoginItemSetEnabled helper to start the App on login?
Following the instructions found here as well as around the Developer Forums, I've successfully managed to begin debugging my system extension on a virtual machine.
Unfortunately, after archiving my system extension and copying over the .app file to my virtual machine, my .app loses access to the source code (on my host machine) and hitting any beak points via lldb yields me hard-to-read assembly code.
How do I also get source code when attaching lldb to a system extension?