agent: fix NoInputNoOutput agents rejecting all pairing requests#190
Open
abezukor wants to merge 1 commit into
Open
agent: fix NoInputNoOutput agents rejecting all pairing requests#190abezukor wants to merge 1 commit into
abezukor wants to merge 1 commit into
Conversation
The Agent documentation states:
Setting all handlers to [None] (the default) will result in a
`NoInputNoOutput` handler that accepts all requests.
This did not work. An all-None Agent published the NoInputNoOutput
capability, but rejected the RequestAuthorization call that BlueZ
makes for incoming just-works pairing, so every pairing attempt
failed. This change makes the code match the documented behavior.
It was also impossible to fix from user code: setting
request_authorization to accept the pairing changed the published
capability to DisplayYesNo, causing MITM-capable peers (e.g. iOS)
to negotiate numeric comparison instead of just-works.
Fix both issues by:
- Excluding request_authorization and authorize_service from the
capability derivation, since both are local authorization policy
decisions that BlueZ invokes regardless of the published input
and output capabilities. Only request_confirmation now implies
yes/no capability.
- Installing default RequestAuthorization and AuthorizeService
handlers that accept all requests when no handlers are set, so
an all-None Agent behaves as documented.
Note for existing code: an agent with only request_authorization or
authorize_service set now publishes NoInputNoOutput instead of
DisplayYesNo, and an all-None agent now accepts incoming just-works
pairing instead of rejecting it, as the documentation always stated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
It is currently impossible to register a working
NoInputNoOutputagent, because the capability string passed toRegisterAgentis derived automatically from which handler fields are set:None→ capability isNoInputNoOutput, but BlueZ still callsRequestAuthorizationfor incoming just-works pairing (andAuthorizeServicefor service connections). Since the handler isNone, bluer replies withorg.bluez.Error.Rejected— so every pairing attempt fails.request_authorizationto accept → the derivation flips the capability toDisplayYesNo, so MITM-capable centrals (iOS in particular sets the MITM flag) negotiate numeric comparison instead of just-works. The phone then shows a "confirm this code" dialog for a code that a headless device cannot display anywhere.This also contradicts the existing
Agentdocumentation, which states:The all-
Noneagent rejected all requests instead, so the documented behavior never worked.Use case
A headless device (no display, no input) exposing a GATT service with
encrypt-read/encrypt-writecharacteristics. Phones must be able to pair via just-works with a single consent tap and no passkey dialog. This requires advertisingNoInputNoOutputand acceptingRequestAuthorization/AuthorizeService.Fix
Exclude
request_authorizationandauthorize_servicefrom the capability derivation. Per the BlueZ agent API,RequestAuthorizationis called "to request the user to authorize an incoming pairing attempt which would in other circumstances trigger the just-works model" — it is a local authorization policy decision, invoked regardless of the published capability, not an input/output capability. The same applies toAuthorizeService. Onlyrequest_confirmationnow implies yes/no capability. With this change, an agent with only authorization handlers correctly advertisesNoInputNoOutput.Make the all-
Nonedefault match the documented behavior. When no handlers are set, defaultRequestAuthorizationandAuthorizeServicehandlers that accept all requests are installed at registration, so the defaultAgentis the documented "NoInputNoOutputhandler that accepts all requests". Code-input requests (RequestPinCode,RequestPasskey, etc.) still reject — BlueZ does not send them to aNoInputNoOutputagent.Behavior changes
request_authorization/authorize_serviceset now publishesNoInputNoOutputinstead ofDisplayYesNo. Such configurations were effectively broken before, since they had norequest_confirmationto satisfy numeric comparison.Noneagent now accepts incoming just-works pairing instead of rejecting it, matching the existing documentation.cargo buildandcargo clippy --all-featurespass with no new warnings.🤖 Generated with Claude Code - I (a Human) did manually verify the issue and audit the code.