Missing prompt for "Input monitoring" for daemons

Hello,


We have a service (daemon) that requires keystroke monitoring and runs under the new "Input Monitoring" policy ( starting from Catalina) .


The problem is when I run the daemon from Xcode I do receive the standard "keystroke receiving" prompt.

When the service runs as a daemon or as a console application there is no "keystroke receiving" prompt at all.

Also, when there are no apps added in the "Input monitoring" list, the button add ("+") is not even shown.

Thus, it's even impossible to add manually our service (daemon) to that policy.

As the result, the service is not functioning at all with the "TCC deny IOHIDDeviceOpen" error.

But I never got any prompts neither for a console app nor for a daemon.


The question is how we must ship the service (daemon) the get the proper "keystroke receiving" prompt?

Our service ships without a bundle, just as a console application with a daemon plist.

As for a test, I tried to create an application bundle doing some IOHIDDeviceOpen requests, and I got the "keystroke receiving" prompt.

But I don't think it will be good to ship the daemon within an application bundle.


Any ideas how get the prompt for a daemon case?

Thanks in advance!

Accepted Reply

Ah, that’s interesting. In general, daemons should not be doing anything with input events because such events are the preserve of the window server (for more background on this, see Technote 2083 Daemons and Agents). However, you’re doing this using

IOHIDDevice
, which is part of I/O Kit, and thus daemon safe.

When the service runs as a daemon or as a console application there is no "keystroke receiving" prompt at all.

Given that you’re running outside of the user context, you’re never going to get a TCC prompt. This was a deliberate design decision. We only issue TCC problems for things that we can specifically relate to the GUI login session.

Also, when there are no apps added in the "Input monitoring" list, the button add ("+") is not even shown.

Right. This is a known bug (r. 55284204).

You should be able to make this work by:

  1. Placing your daemon code inside an app.

  2. Having it launchable as an app from the GUI.

  3. At this point it can trigger the Input Monitoring TCC request. If the user OKs that, it’ll apply to all further instances of that code running.

  4. Having your daemon launch the app with an argument that causes it to enter daemon mode.

I’ve played around with this a lot in similar contexts and it has worked well for me. I have not, however, tested your specific case, that is, a daemon using

IOHIDDevice
. You should give a whirl and see how you get along.

Finally, a word of caution about daemon placement. A daemon runs as root, and thus it’s not safe to locate the daemon within

/Applications
, either directly or within another app. Doing so will create an unchecked admin-to-root privilege escalation security vulnerability (because
/Applications
is writeable by all admin users, so an admin user can move your daemon out of the way and replace it with their own code without any authentication).

As to where you /should/ place your daemon, that kinda depends on the nature of your product as a whole. My general recommendation is

/Library/Application Support/***/MyDaemon.app
, where
***
contains either your company or product name. This recommendation is based on a number of criteria:
  • The

    /Library/Application Support
    directory is only writeable by root.
  • The

    ***
    ensures that you don’t collide with other products.
  • The daemon is visible in the Finder, so the user can launch it in order to trigger a TCC prompt, as I discussed above.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

To start, what exactly do you mean by daemon? How is your code launched?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Quinn,


Sorry, I forgot to mention this. I have a Launch Daemon with a plist file located in /Library/LaunchDaemons.

The plist is quite straightforward:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"

"http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>Label</key>

<string>com.test.hardwareservice</string>

<key>ProgramArguments</key>

<array>

<string>/Library/LaunchDaemons/Test/HardwareService</string>

<string>start</string>

</array>

<key>RunAtLoad</key>

<true/>

<key>KeepAlive</key>

<true/>

</dict>

</plist>


HardwareService is based on the POCO library. It uses a ServerApplication class, and can work from the command line or as

a service or daemon. Technically, HardwareService is a console application.

I've tried to create a test console application without Poco that calls IOHIDDeviceOpen, the prompt ("keystroke receiving") was missing as well. The prompt is only shown when I call IOHIDDeviceOpen from a test application with a bundle.

Ah, that’s interesting. In general, daemons should not be doing anything with input events because such events are the preserve of the window server (for more background on this, see Technote 2083 Daemons and Agents). However, you’re doing this using

IOHIDDevice
, which is part of I/O Kit, and thus daemon safe.

When the service runs as a daemon or as a console application there is no "keystroke receiving" prompt at all.

Given that you’re running outside of the user context, you’re never going to get a TCC prompt. This was a deliberate design decision. We only issue TCC problems for things that we can specifically relate to the GUI login session.

Also, when there are no apps added in the "Input monitoring" list, the button add ("+") is not even shown.

Right. This is a known bug (r. 55284204).

You should be able to make this work by:

  1. Placing your daemon code inside an app.

  2. Having it launchable as an app from the GUI.

  3. At this point it can trigger the Input Monitoring TCC request. If the user OKs that, it’ll apply to all further instances of that code running.

  4. Having your daemon launch the app with an argument that causes it to enter daemon mode.

I’ve played around with this a lot in similar contexts and it has worked well for me. I have not, however, tested your specific case, that is, a daemon using

IOHIDDevice
. You should give a whirl and see how you get along.

Finally, a word of caution about daemon placement. A daemon runs as root, and thus it’s not safe to locate the daemon within

/Applications
, either directly or within another app. Doing so will create an unchecked admin-to-root privilege escalation security vulnerability (because
/Applications
is writeable by all admin users, so an admin user can move your daemon out of the way and replace it with their own code without any authentication).

As to where you /should/ place your daemon, that kinda depends on the nature of your product as a whole. My general recommendation is

/Library/Application Support/***/MyDaemon.app
, where
***
contains either your company or product name. This recommendation is based on a number of criteria:
  • The

    /Library/Application Support
    directory is only writeable by root.
  • The

    ***
    ensures that you don’t collide with other products.
  • The daemon is visible in the Finder, so the user can launch it in order to trigger a TCC prompt, as I discussed above.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"