Can I open a DriverKit user client with a daemon? - macos

I'm building a virtual HID device in Driver Kit.
I was wanting to communicate with the virtual device driver from a daemon, as the daemon is necessary for generating the HID events that would be sent from the driver.
I'm matching on my driver service fine via the daemon, however when I attempt to open the service I get -536870174 which from what I see here means kIOReturnNotPermitted.
From what I've read about this, the device driver user client can only be opened via an application that has been granted the com.apple.developer.driverkit.userclient-access entitlement by Apple.
So, my question:
Is opening the user client of a device driver in Driver Kit with a daemon completely out of the question?
Is my only option here to have a intermediate application with the com.apple.developer.driverkit.userclient-access entitlement, which can act as a broker between the daemon and the driver?
So it would be something like:
Daemon <---xpc connection--> Intermediate App <--- user client ---> Virtual HID device
Edit:
To add to Phil's answer below regarding running an app as a daemon, there's some Apple written guidance here.

The short of it: there's no need to go to such lengths. Daemons can have entitlements.
The official solution is to make your daemon an application, from the OS's point of view. Essentially, build your daemon as an application bundle, just don't call it .app. Your launchd plist can point at the embedded executable and it will inherit the surrounding package's entitlements.
Unofficially, I've also had success with directly embedding the Info.plist in the daemon's binary, without a surrounding bundle directory. This is commonly done for privileged helper tools for use with SMJobBless() and is described in a bunch of places including here. Essentially, enable the "Create Info.plist Section in Binary" build setting in Xcode, or add -sectcreate __TEXT __info_plist path/to/Info.plist to your linker flags when using another build system. However, I didn't end up shipping this (didn't need it in the end) so I only ever tested it with SIP disabled. So I currently can't say for certain if it will work when signed with an appropriate provisioning profile.
Both methods allow you to give the binary an application bundle ID by specifying it in the Info.plist, which then also enables you to include an entitlements file during code signing. As com.apple.developer.driverkit.userclient-access is not one of the "open" entitlements from the com.apple.security.* namespace, you'll also need a provisioning profile that includes it, or the OS won't accept it with SIP enabled.

If you take the path of running an app as a daemon as in the apple resource stated above, the following code is a swift variation of the objective-c that Eskimo has posted.
import Foundation
import Security
import OSLog;
var me:SecCode? = nil;
let kSecCSDefaultFlags:SecCSFlags = SecCSFlags(rawValue: SecCSFlags.RawValue(0))
var err = SecCodeCopySelf(kSecCSDefaultFlags, &me);
assert(err == errSecSuccess)
var infoCF:CFDictionary? = nil;
var staticMe:SecStaticCode? = nil;
err = SecCodeCopyStaticCode(me!, kSecCSDefaultFlags, &staticMe)
assert(err == errSecSuccess);
err = SecCodeCopySigningInformation(staticMe!, kSecCSDefaultFlags, &infoCF);
assert(err == errSecSuccess);
print(infoCF);
os_log("%{public}s", infoCF.debugDescription);

Related

How to pass user setting to Driver Extension (MacOS)?

I am writing a driverkit extension whose goal is to block some categories of USB devices, such as flash drives. The driver should block (match to) any device of the relevant device classes, except those, which are whitelisted (based on their vendor and product ID). The whitelist can be set dynamically by user of the application.
The question is, how to pass these data to the driver as reading from a file or something like Windows registry is not available in the DriverKit. The tricky part is that the driver requires the whitelist data before the device is matched.
From what I understood, rejection of device is possible by returning an error from Start() method and returning from it prematurely. I got an idea to send the data while the driver is running this function, however this is not possible as the communication via IOUserClass is not available until the Start method returns.
Is this somehow doable?
As far as I'm aware, communicating with user space apps from the initial Start() method is not possible from DriverKit extensions. As you say, IOUserClients are the mechanism to use for user space communication, and those aren't available until the service is started and registered. You can have your driver match IOResources/IOUserResources so it is always loaded, but each matched service starts up an independed process of your dext, and I'm not aware of a way to directly communicate between these instances.
If I understand you correctly, you're trying to block other drivers from acquiring the device. I don't think the solution you have in mind will help you with this. If you return success from Start(), your dext will drive the device. If you return failure, no driver is loaded for the device, because matching has already concluded. So other drivers would never get a chance anyway, regardless of whether the device is on your allow-list or deny-list.
It's new in DriverKit 21 (i.e. macOS Monterey), and I've not had a chance to try it yet, but there is an API for reading files, OSMappedFile. I would imagine that the DriverKit sandbox will have something to say about which files a dext can open, but this seems like an avenue worth exploring whether you can open configuration files this way.
Note that none of this will help you during early boot, as your dext will never be considered for matching at that time. And you may not be able to get required entitlements from Apple to build a dext which matches USB device classes rather than specific product/vendor ID patterns. (Apologies for repeating myself, but other users may come across this answer and not be aware of this issue.)

WebView in MacOS: How to request file system permissions correctly

It may be because I'm not developing a traditional Swift app, instead I'm using the https://github.com/zserge/webview library to develop a cross platform app.
My app has 2 parts, divided into 2 different threads: one thread launches the window and displays a JS app. The second thread contains a background server bound to an ephemeral port and serves a json api, written in Rust. The Rust side is also the one talking with the File System and making all requests. On Linux I don't have any problem, but on Mac it works only when requesting resources from root and home directories but not from Documents/Desktop etc
The first problem I had when running it on Mac has been allowing access to the server from the window: I had to add a new entry to the info.plist file, according to this answer in stackoverflow: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection
This resolved the issue I had talking to the server bound to the ephemeral port. Truth be said I have to also say that now it requests me to allow access to external resources every time I launch the app.
But, when trying to execute a function which requires access from the Rust side to the Desktop (for example), it doesn't work and it does not show any popup and never did
Btw, if you want to have a look at the final product, maybe to help you understand better the app, have a look here: http://getdevspace.com/
Check the ch mode file system
Even had the same problem so i checked the ch mode so it worked
Thanks

Retrieving Sandbox entitlements from an XPC bundle

It's usually possible to see if sandbox entitlements exist for an application with the codesign command line call. For example, calling this
codesign --display --entitlements :- /Applications/Notes.app/ | grep sandbox
Will result in this output
Executable=/Applications/Notes.app/Contents/MacOS/Notes
<key>com.apple.security.app-sandbox</key>
Where the key com.apple.security.app-sandbox denotes that the app runs in a sandbox.
On Mavericks, some XPC helper apps show in Activity Monitor to be running in a sandbox, but calling codesign on them doesn't reveal anything. An example for one of Safari's XPC helper applications: -
codesign -display --entitlements :- /System/Library/PrivateFrameworks/WebKit2.framework/Versions/A/XPCServices/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent | grep sandbox
Just returns this: -
Executable=/System/Library/PrivateFrameworks/WebKit2.framework/Versions/A/XPCServices/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent
I've also tried checking programatically if the bundle is sandboxed, with the code described in this article, but again, returns as not sandboxed, even though Activity Monitor clearly states that it is.
Is it possible to check such an xpc bundle, to see if will be executed in a sandbox and uses sandbox entitlements?
Looking for the com.apple.security.app-sandbox entitlement as you're doing is the way to check if an XPC service uses App Sandbox. The reason that the Safari Web Content process shows up as sandboxed despite not having this entitlement is that it does not use App Sandbox, but rather lower level interfaces to the underlying sandbox facility in OS X. Early on the web content process calls WebKit's initializeSandbox() method, which uses a system private interface to apply a sandbox policy at runtime. So finding out whether a given XPC service will create a sandboxed process is as difficult as finding out whether that service will call a function. However, if you're curious about the restrictions placed on such processes the sandbox policy is usually stored in a .sb file somewhere on the system. In this case it's at /System/Library/PrivateFrameworks/WebKit2.framework/Versions/A/Resources/com.apple.WebProcess.sb.

Using a LaunchAgent inside the Mac app sandbox

How would you use a LaunchAgent inside the sandbox? I want to distribute a UI-less LaunchAgent app bundle inside my main application that I can launch on demand. The reason I want this instead of an XPC service is for the KeepAlive option, which will prevent launchd from automatically killing my process when its idle. This option doesn't exist on XPC services.
The documentation says that there's a plist that needs to be copied into ~/Library/LaunchAgents, and this is obviously not possible inside the sandbox. Is there some sort of system API that would handle copy the plist for me? I've seen Apple's SMJobBless sample code, but it seems like that's for registering a privileged LaunchDaemon rather than an unprivileged LaunchAgent.
The API you're looking for is SMLoginItemSetEnabled(). You'll have to package your long-running agent as a regular .app bundle and put it into your main app's bundle at Content/Library/LoginItems.
Then at runtime in the main application you can use the SMLoginItemSetEnabled() call with the agent's bundle identifier to enable and disable your agent. If your main application quits, the agent stays alive. If the user logs out and back in or reboots, the OS will relaunch your agent when the user logs back in.
Update: As Dmitry notes, the documentation no longer comes with the sample I originally mentioned, but another Apple sample code project, for App Sandbox, demonstrates the same API (see the file NSXPCConnection+LoginItem.m).
Documentation page now doesn't contain a sample project, so here it is: http://rhult.github.io/articles/sandboxed-launch-on-login/

Determine if WindowServer is available?

I'm writing a framework for OS X that may be used by applications with or without a connection to the OS X WindowServer (i.e. both GUI apps and command-line apps run via, e.g. an ssh session). One class in the framework is for tracking files in the user's home folder across netework and mobile home directories (on OS X, users can have their home directory served via AFP from a server--a "network home directory"--or have the same home folder syncrhonized for offline access--a "mobile home directory").
Because I need to track files across filesystems, we've chosen to use relative paths, rather than OS X aliases (or the 10.6 NSURL bookmarks). When a file can't be found, I need to ask the user for input to relocate that file (like how the Alias Manager prompts the user to reconnect a broken alias). If the application has (or can make) a connection to the WindowServer, this is as simple as using an NSOpenPanel. However, when the app can't make a connection to the WindowServer I need to use an alternative method to get the user input.
So, how can I tell which method to use from within the famework code? Is there a way to programatically determine if a WindowServer connection is available (or possible)?
I recognize that an alternative architecture, where the framework client provides a callback mechanism to prompt the user would let the input-gathering strategy be provided by the calling application. I'd like to make things as simple as possible for the calling application, however, so my first choice would be to encapsulate these details in the framework if I can.
There's an environment variable named SECURITYSESSIONID that is set by loginwindow.app and get's passed to the user's applications. The variable is not set if you login via ssh. It serves as kind of a handle to talk to the window server.
Problem: The existence of this variable does not mean that this user currently controls the window manager (think fast user switching).
There's a function called CGSessionCopyCurrentDictionary in the ApplicationServices framework which looks promising:
Return Value: A window server session dictionary, or NULL if the caller is not running within a Quartz GUI session or the window server is disabled. You should release the dictionary when you are finished using it. For information about the key-value pairs in this dictionary, see "Window Server Session Properties."

Resources