Access Bluetooth in system daemon - Unauthorized

Hello, as the title says, I am trying to access Bluetooth in a system daemon. I am running on MacOS Sonoma 14.5. When initializing Bluetooth, my daemon received Unauthorized state. I have tried to add my daemon in the system settings (System Preferences > Security & Privacy > Privacy > Bluetooth) "Allow applications to access Bluetooth" by adding the program executable path defined by the entry Program of my system daemon as suggested here: https://developer.apple.com/forums/thread/662459. But I am still having the issue.

Writing a system daemon with Bluetooth is not my final goal. The bigger picture is the smartcard reader driver with Bluetooth access which as the same issue and the solution is probably related. I do not remember how but my smartcard reader driver use to work with Bluetooth but it does now with the same Unauthorized error.

As far as I can see daemon and smartcard drivers does not have support for entitlement.

Here are the logs for my sample system daemon:

my_daemon	[0x6000011b0000] activating connection: mach=true listener=false peer=false name=com.apple.server.bluetooth.le.att.xpc
bluetoothd	[0x7f804828e8a0] activating connection: mach=false listener=false peer=true name=com.apple.server.bluetooth.le.att.xpc.peer[76672].0x7f804828e8a0
bluetoothd	Received XPC message "CBMsgIdCheckIn" from session ""
bluetoothd	Received XPC check-in from session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67" fAccessLevel 0 fProgrammaticPairing 0 fLimitedForMediaAccess 0
bluetoothd	Access level is less than kXPCAccessLevelSystem for session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67". Restricted state operation not allowed
bluetoothd	Sending 'session attached' event for session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67"
bluetoothd	Attached session for "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67" with session: 0x7f804802d1b0, session handle: 0xef8d0000
bluetoothd	Registering peripheral session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67" with backgrounding: off, persistence: off (CBSR) restoreID: (null)
bluetoothd	Error getting Application State for <private>: <private>, 3
bluetoothd	Error getting Application State for <private>: <private>, 3
bluetoothd	Session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67" tccRequired : 1
bluetoothd	ReadyForTCC. TCC required:1 fLimitedForMediaAccess:0 fDeviceAccessForMediaExtension:0
bluetoothd	Session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67" : needsRestrictedStateOperation = 0, overrideRestrictedState = 0 , denylistMode = 0, receivesControllerBTClockEvents=0
my_daemon	Received CBMsgIdReadyForTCC
my_daemon	Running performTccCheck CBManager tccAvail 1, tccRequired 1
my_daemon	TCC required
my_daemon	[0x6000011b8000] activating connection: mach=true listener=false peer=false name=com.apple.tccd
my_daemon	[0x6000011b8000] failed to do a bootstrap look-up: xpc_error=[3: No such process]
my_daemon	[0x6000011b8000] invalidated after a failed init
my_daemon	send_message_with_reply(): user tccd unavailable, sending 0x600000ab4000 to system tccd
my_daemon	[0x6000011b4000] activating connection: mach=true listener=false peer=false name=com.apple.tccd.system
tccd	[0x7fd4d1f7ed80] activating connection: mach=false listener=false peer=true name=com.apple.tccd.system.peer[76672].0x7fd4d1f7ed80
tccd	REQUEST: tccd_uid=0, sender_pid=76672, sender_uid=0, sender_auid=-1, function=TCCAccessRequest, msgID=76672.1
tccd	AUTHREQ_CTX: msgID=76672.1, function=<private>, service=kTCCServiceBluetoothAlways, preflight=no, query=1, client_dict=(null), daemon_dict=<private>
tccd	AUTHREQ_ATTRIBUTION: msgID=76672.1, attribution={requesting={TCCDProcess: identifier=my_daemon-5555494498236e3b5e2e395b93c13af176769937, pid=76672, auid=0, euid=0, binary_path=/Users/olivier/daemon/my_daemon}, },
tccd	AUTHREQ_SUBJECT: msgID=76672.1, subject=/Users/olivier/daemon/my_daemon,
tccd	Refusing TCCAccessRequest for service kTCCServiceBluetoothAlways from client Sub:{/Users/olivier/daemon/my_daemon}Resp:{TCCDProcess: identifier=my_daemon-5555494498236e3b5e2e395b93c13af176769937, pid=76672, auid=0, euid=0, binary_path=/Users/olivier/daemon/my_daemon} in background session
tccd	AUTHREQ_RESULT: msgID=76672.1, authValue=0, authReason=5, authVersion=1, error=(null),
tccd	REPLY: (0) function=TCCAccessRequest, msgID=76672.1
my_daemon	[0x6000011b4000] invalidated after the last release of the connection object
bluetoothd	Received XPC message "CBMsgIdTCCDone" from session "my_daemon-5555494498236e3b5e2e395b93c13af176769937-peripheral-76672-67"
tccd	[0x7fd4d1f7ed80] invalidated after getting a no-senders notification - client is gone
bluetoothd	[0x7f80482820f0] activating connection: mach=true listener=false peer=false name=com.apple.tccd.system
tccd	[0x7fd4d32585f0] activating connection: mach=false listener=false peer=true name=com.apple.tccd.system.peer[169].0x7fd4d32585f0
tccd	REQUEST: tccd_uid=0, sender_pid=169, sender_uid=0, sender_auid=-1, function=TCCAccessRequest, msgID=169.48
tccd	[0x7fd4d313d880] activating connection: mach=true listener=false peer=false name=com.apple.tccd
tccd	[0x7fd4d313d880] failed to do a bootstrap look-up: xpc_error=[3: No such process]
bluetoothd	[0x7f80482820f0] invalidated after the last release of the connection object
bluetoothd	Bluetooth user permission alwaysAuth: denied
tccd	[0x7fd4d313d880] invalidated after a failed init
tccd	FORWARD: to=com.apple.tccd/0, request: {
	require_purpose=<xpc_null>
	service="kTCCServiceBluetoothAlways"
	function="TCCAccessRequest"
	preflight=true
	target_token={pid:76672, auid:-1, euid:0}
	TCCD_MSG_ID="169.48"
	background_session=false
}
tccd	REPLY: from=com.apple.tccd, reply: {
	XPCErrorDescription="Connection invalid"
}
tccd	forwardMessage error: Connection invalid.
tccd	[0x7fd4d3152bf0] activating connection: mach=false listener=false peer=true name=com.apple.tccd.system.peer[169].0x7fd4d3152bf0
bluetoothd	[0x7f80482820f0] activating connection: mach=true listener=false peer=false name=com.apple.tccd.system
tccd	REQUEST: tccd_uid=0, sender_pid=169, sender_uid=0, sender_auid=-1, function=TCCAccessRequest, msgID=169.49
tccd	[0x7fd4d32585f0] invalidated after getting a no-senders notification - client is gone
tccd	[0x7fd4d1f4c810] activating connection: mach=true listener=false peer=false name=com.apple.tccd
tccd	[0x7fd4d1f4c810] failed to do a bootstrap look-up: xpc_error=[3: No such process]
tccd	[0x7fd4d1f4c810] invalidated after a failed init
tccd	FORWARD: to=com.apple.tccd/0, request: {
	require_purpose=<xpc_null>
	service="kTCCServiceBluetoothAlways"
	function="TCCAccessRequest"
	preflight=true
	target_token={pid:76672, auid:-1, euid:0}
	TCCD_MSG_ID="169.49"
	background_session=false
}
tccd	REPLY: from=com.apple.tccd, reply: {
	XPCErrorDescription="Connection invalid"
}
tccd	forwardMessage error: Connection invalid.
Answered by Engineer in 792918022

Accessing Bluetooth is protected behind user level TCC (Transparency, Consent, and Control) prompts, which requires the requesting app to be running in a user context.

Therefore is not possible to use Bluetooth from a system level daemon.

Your best bet would be to separate the Bluetooth functionality into a user level component, perhaps a user agent, and communicate between your daemon and your process interacting with Bluetooth. If there are specific actions the daemon needs to take, it can delegate them to the user level process.

These are somewhat recent changes to Bluetooth security, so you may have observed the driver working before they have been implemented.

Accessing Bluetooth is protected behind user level TCC (Transparency, Consent, and Control) prompts, which requires the requesting app to be running in a user context.

Therefore is not possible to use Bluetooth from a system level daemon.

Your best bet would be to separate the Bluetooth functionality into a user level component, perhaps a user agent, and communicate between your daemon and your process interacting with Bluetooth. If there are specific actions the daemon needs to take, it can delegate them to the user level process.

These are somewhat recent changes to Bluetooth security, so you may have observed the driver working before they have been implemented.

If the user gives access to Bluetooth to the main app, does the containing daemon or system extension inherit the permission?

Access Bluetooth in system daemon - Unauthorized
 
 
Q