With some help from a contributor (Lukasa) I was able to adjust the example to work on a daemon.
The problem was that the example binds itself to the stdout (writing the response of the ssh command to it). As the stdout is bound to a file in a daemon and the bootstrap includes a check that the output is not a file, an error would occur.
For anyone else experiencing this issue in the future, I've updated the reproduction to include the solution: https://github.com/eliaSchenker/nio-ssh-daemon-issue/tree/main
Post
Replies
Boosts
Views
Activity
After some further debugging I found that the thrown error happens in Bootstrap.swift in the method validateFileDescriptorIsNotAFile. This method is called by _takingOwnershipOfDescriptors (same file). The input and output for the ownership are 0 and 1, the validation for the input passes, the one for the output fails at the following line:
private func validateFileDescriptorIsNotAFile(_ descriptor: CInt) throws {
// ...
var s: stat = .init()
try withUnsafeMutablePointer(to: &s) { ptr in
try Posix.fstat(descriptor: descriptor, outStat: ptr)
}
switch s.st_mode & S_IFMT {
case S_IFREG, S_IFDIR, S_IFLNK, S_IFBLK:
throw ChannelError.operationUnsupported // Here the error occurrs
default:
() // Let's default to ok
}
When running the application in the agent, the st_mode is 33188, when running it normally in Xcode, it is 8592.
For anyone else experiencing this issue, we finally figured it out:
In the end, this is the command we were using to sign our extensions with (incorrect command):
codesign -s "Developer ID Application: XXXXXXXXX (XXXXXXX)" -f --timestamp -o runtime "OurApp.app/Contents/PlugIns/OurExtension.app"
After some digging in the Console, we found that the extensions were being closed by the gatekeeper with the following message:
[/Applications/Extension_With_Signing_Demo.app/Contents/PlugIns/findersynctest.appex]: plug-ins must be sandboxed
After discovering this message, the cause of the error was pretty clear for us: Because we hadn't deemed it necessary to actually pass entitlements while signing the extension (because we thought that they didn't require any) and we didn't know that extensions require sandbox to run.
To solve the issue, all we did was add an entitlements plist file containing some of the basic entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
and included them in our command
codesign -s "Developer ID Application: XXXXXXXXX (XXXXXXX)" -f --timestamp -o runtime --entitlements EntitlementsFile.entitlements "OurApp.app/Contents/PlugIns/OurExtension.app"
After that, the extensions started as intended.
Summary
The solution to our problem was the fact that we didn't pass any entitlements when signing the extensions, even though extensions require the sandbox entitlement.
A very helpful resource on how to add entitlements when signing an app or app extension (and code signing in general): Creating Distribution-Signed Code for Mac
Hi there,
Thanks for your quick reply!
Unfortunately the file I provided to you was incorrect (we were testing some things with the signature before and left it in the script by accident).
I have updated the file in the repository with the correct command (its essentially the same command as in the export_without_extension_signing.sh file)
I’m not sure what codesign does in that case, but it’s unlikely to be good.
With this codesign would not have run at all and therefore not sign anything. The problem mentioned in the original post still occurs even when the signing commands are all executed correctly.
Hey, thanks for your answer. Yes, selectedItemURLs() is returning a value. The whole startAccessingSecurityScopedResource() workflow does not work, because the operating system thinks that the provided urls are not selected by the user selected (and therefore not available to the sandbox). I am pretty sure that this is an issue of the operating system not recognising that these files are indeed user selected and should therefore be accessible to the extension.
After some more research, I found that it is actually possible to achieve something like this.
Opening the file under /System/Library/PreferencePanes/Extensions.prefPane will open the Extensions Pane at the uppermost level. A way to do this is using the NSWorkspace:
let prefpaneUrl = URL(fileURLWithPath: "/System/Library/PreferencePanes/Extensions.prefPane")
NSWorkspace.shared.open(prefpaneUrl)
After searching through some old forum-posts where this problem is also described, I do not think it is possible.
If you have multiple extensions you only have two options:
1 - Simply open the preferences (it is not possible to open the Extensions part of the preferences, for some reason) using the following code:
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference")!)
You should display some instructions for the user, telling them where and how to enable your extensions (which is what I resulted to)
2- Result to opening only the config of the Finder Sync Extension using:
FIFinderSyncController.showExtensionManagementInterface()
This is suboptimal though, because the user might only enable one of your extensions.
I really hope that some implementation of opening the extension preferences will be added soon, or that some of the older extension (e.g. Share Extension, in my case) get updated to also include a Controller similar to the FIFinderSyncController of the Finder Sync extension.