问题
I am trying to create a client connection to a SystemExtension IOService. I can see that my IOUserClient
subclass is created (init()
and Start(IOService*)
is called), but the return code from IOServiceOpen
returns kIOReturnNotPermitted
.
I am making the call to IOServiceOpen
from the same app that creates the activation request.
Entitlements for app that makes the activation request / call to IOServiceOpen
:
<?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.developer.driverkit.userclient-access</key>
<array>
<string>sc.example.MyUserUSBInterfaceDriver</string>
</array>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.system-extension.uninstall</key>
<true/>
</dict>
</plist>
Entitlements for the dext:
<?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.developer.driverkit.userclient-access</key>
<array>
<string>sc.example.USBApp</string>
</array>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.transport.usb</key>
<true/>
</dict>
</plist>
MyUserClient:
#ifndef MyUserClient_h
#define MyUserClient_h
#include <DriverKit/IOUserClient.iig>
class MyUserClient : public IOUserClient {
public:
bool init() override;
kern_return_t Start(IOService* provider) override;
kern_return_t Stop(IOService* provider) override;
void free() override;
};
#endif /* MyUserClient_h */
bool MyUserClient::init() {
LOG();
if (!super::init()) {
LOG("super::init() failed");
return false;
}
return true;
}
kern_return_t IMPL(MyUserClient, Start) {
LOG();
auto ret = Start(provider, SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
LOG("SUPERDISPATCH Start failed, ret: %{public}d", ret);
}
return ret;
}
kern_return_t IMPL(MyUserClient, Stop) {
LOG();
auto ret = Stop(provider, SUPERDISPATCH);
if (ret != kIOReturnSuccess) {
LOG("SUPERDISPATCH Stop failed, ret: %{public}d", ret);
}
return ret;
}
void MyUserClient::free() {
super::free();
LOG();
}
LOG
is just a macro that does os_log(OS_LOG_DEFAULT, ...
NewUserClient
implementation:
kern_return_t IMPL(MyUserUSBInterfaceDriver, NewUserClient) {
LOG("%{public}d:", type);
IOService* client;
auto ret = Create(this, "UserClientProperties", &client);
*userClient = OSDynamicCast(IOUserClient, client);
if (!(*userClient) || ret != kIOReturnSuccess) {
LOG("Failed to create IOUserClient, %{public}d", ret);
}
return ret;
}
Code to connect to system extension:
void connectToDext(io_service_t* serviceObject) {
io_connect_t dataPort;
kern_return_t kernResult =IOServiceOpen(*serviceObject, mach_task_self(), 123, &dataPort);
if (kernResult != KERN_SUCCESS) {
printf("IOServicceOpen failed: %d, %s\n", kernResult, kern_return_t_toCStr(kernResult));
}
kernResult = IOServiceClose(dataPort);
if (kernResult != KERN_SUCCESS) {
printf("IOServicceClosed failed: %d, %s\n", kernResult, kern_return_t_toCStr(kernResult));
}
}
int connectToFirstDext() {
CFMutableDictionaryRef matchingDict;
matchingDict = IOServiceMatching("IOService");
if (matchingDict == 0) {
return -1;
}
io_iterator_t iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter) !=
KERN_SUCCESS) {
printf("IOServiceGetMatchingServices failed.\n");
return -1;
}
io_service_t device;
while ((device = IOIteratorNext(iter))) {
io_name_t deviceName;
if (IORegistryEntryGetName(device, deviceName) == KERN_SUCCESS) {
printf("name: %s\n", deviceName);
if (strcmp(deviceName, "MyUserUSBInterfaceDriver") == 0) {
printf("Calling connect\n");
connectToDext(&device);
}
}
IOObjectRelease(device);
}
IOObjectRelease(iter);
return 0;
}
Edit:
Info.plist of app
<?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>BuildMachineOSBuild</key>
<string>19E287</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>USBApp</string>
<key>CFBundleIdentifier</key>
<string>sc.example.USBApp</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>USBApp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>11E503a</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>19E258</string>
<key>DTSDKName</key>
<string>macosx10.15</string>
<key>DTXcode</key>
<string>1141</string>
<key>DTXcodeBuild</key>
<string>11E503a</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Example. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticTermination</key>
<true/>
<key>NSSupportsSuddenTermination</key>
<true/>
</dict>
</plist>
Info.plist of dext:
<?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>BuildMachineOSBuild</key>
<string>19E287</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>CFBundleIdentifier</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>CFBundlePackageType</key>
<string>DEXT</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>11E503a</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string></string>
<key>DTSDKName</key>
<string>driverkit.macosx19.0</string>
<key>DTXcode</key>
<string>1141</string>
<key>DTXcodeBuild</key>
<string>11E503a</string>
<key>IOKitPersonalities</key>
<dict>
<key>example_device</key>
<dict>
<key>CFBundleIdentifier</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOProviderClass</key>
<string>IOUSBHostInterface</string>
<key>IOUserClass</key>
<string>MyUserUSBInterfaceDriver</string>
<key>IOUserServerName</key>
<string>sc.example.MyUserUSBInterfaceDriver</string>
<key>UserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOServiceDEXTEntitlements</key>
<string></string>
<key>IOUserClass</key>
<string>MyUserClient</string>
</dict>
<key>bConfigurationValue</key>
<integer>1</integer>
<key>bInterfaceNumber</key>
<integer>0</integer>
<key>idProduct</key>
<integer>8</integer>
<key>idVendor</key>
<integer>1234</integer>
</dict>
</dict>
<key>OSBundleUsageDescription</key>
<string>Example user space USB driver</string>
<key>OSMinimumDriverKitVersion</key>
<string>19.4</string>
</dict>
</plist>
回答1:
Updated answer:
The IOServiceDEXTEntitlements
property in your IOKitPersonality's Info.plist
's user client property dictionary should be one of:
- Missing entirely; this means: don't check client entitlements beyond basics listed below in my original answer.
- I think, an array of arrays of strings; the client must have all the entitlements listed in one of the inner arrays. (NB: I haven't done any testing with this latter case.)
A non-array or empty-array IOServiceDEXTEntitlements
property means the client can't match the entitlements in one of the inner arrays, as there aren't any. The check therefore always fails with kIOReturnNotPermitted
. This is what's happening with your (empty) string value.
For details, check the code in IOUserServer::checkEntitlements()
and where it's called from in IOUserServer::serviceNewUserClient()
in IOUserServer.cpp from the xnu source code.
Original answer:
(Given prior to the contents of the Info.plist
s being posted.)
I don't see anything obviously wrong with what you've posted, so I suspect your problem might be caused by something you haven't posted. Things to watch out for:
- The user client app's
com.apple.developer.driverkit.userclient-access
array must contain the dext's bundle ID. Perhaps double-check that this ID really is equal tosc.example.MyUserUSBInterfaceDriver
? (I've previously spent over an hour tearing my hair out over thekIOReturnNotPermitted
error when I had a typo here.) - The user client app must be permitted to talk to IOKit. This is the case by default, unless your app is sandboxed. Assuming the entitlements you've posted are complete, your app isn't sandboxed, so that shouldn't be the problem.
- If the dictionary used for creating the new user client contains the
IOServiceDEXTEntitlements
key, the user space app must have all entitlements listed there. (This dictionary comes from the dext's info.plist, which you haven't posted yet at time of writing.) - I believe the dext and the app must be signed using the same team ID. (I seem to remember there is an entitlement you can set on the dext to work around this requirement however.) Perhaps double-check the
TeamIdentifier
usingcodesign -dv path/to/your.dext
andcodesign -dv path/to/your.app
回答2:
Tangential to your question - I'll try to answer your actual question separately - but I noticed you're locating your service by iterating over all IOService
objects. You can find it much more elegantly by matching it directly using property matching. I use a helper function for generating the matching dictionary that looks something like this:
// Creates IOKit matching dictionary for locating driverkit-based service objects
// (Corresponds to kext services' IOServiceMatching())
static CFMutableDictionaryRef user_service_matching(CFStringRef driverkit_classname, CFStringRef driverkit_server_bundle_id) CF_RETURNS_RETAINED
{
CFMutableDictionaryRef match = IOServiceMatching("IOUserService");
CFTypeRef match_property_keys[] = { CFSTR("IOUserClass"), kIOBundleIdentifierKey };
CFTypeRef match_property_values[] = { driverkit_classname, driverkit_server_bundle_id };
CFDictionaryRef match_properties = CFDictionaryCreate(
kCFAllocatorDefault,
match_property_keys, match_property_values, 2,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(match, CFSTR(kIOPropertyMatchKey), match_properties);
CFRelease(match_properties);
return match;
}
This matches:
- Objects of type
IOUserService
(This is a much smaller search space thanIOService
and all directIOService
subclasses in dexts are actuallyIOUserService
objects in the "real" (kernel) I/O Registry.) - With the
IOUserClass
property matching the provideddriverkit_classname
(this would beCFSTR("MyUserUSBInterfaceDriver")
in your case) - With the
CFBundleIdentifier
matching the provided one. (CFSTR("sc.example.MyUserUSBInterfaceDriver")
)
Although relatively unlikely, the service name, as retrieved via IORegistryEntryGetName
in the code you posted, could in theory collide with other kexts or dexts, whereas there should be no ambiguity in the bundle identifier, and matching the user class is helpful when your driver implements multiple classes.
来源:https://stackoverflow.com/questions/61613403/kioreturnnotpermitted-from-ioserviceopen-connecting-to-systemextension-ioservice