Files
n_signer/plans/role_as_password_default.md

4.3 KiB

Plan: Role-as-Password as the Default Authorization Model

Status: Draft — ready for review

Problem

The signer currently defaults to requiring interactive approval for every request, even for the default "main" role. This contradicts the intended design where knowing the role name is sufficient authorization (role-as-password). Users running v0.1.21 are prompted to approve requests when they should be authorized automatically.

Root cause

Three places in the code set requires_approval = 1 by default:

Location Context Current value
src/main.c:3816 Default "main" role (non-interactive mode) requires_approval = 1
src/main.c:2374 Wizard role creation prompt Default 1 (Y/n)
src/main.c:2223 Wizard OTP role creation prompt Default 1 (Y/n)

Changes required

1. Default "main" role — non-interactive mode

File: src/main.c:3816

Change:

role.requires_approval = 1;

To:

role.requires_approval = 0;  /* role-as-password: knowing the role name is sufficient */

This is the most critical fix — it affects every user running in non-interactive mode (e.g., --listen unix, --listen tcp, --listen qrexec).

2. Wizard role creation — remove approval prompt

File: src/main.c:2359-2377

Currently the wizard asks:

Require interactive approval for each request? [Y/n]:

With role-as-password as the default, this prompt should be removed entirely. The role is created with requires_approval = 0. If a user wants approval, they can use --preapprove or manually edit the role after creation.

Remove the prompt block (lines 2359-2377) and set:

int requires_approval = 0;  /* role-as-password by default */

3. Wizard OTP role creation — remove approval prompt

File: src/main.c:2208-2226

Same change as #2. Remove the approval prompt for OTP roles and default to requires_approval = 0.

4. Policy table default — no change needed

File: src/policy.c:1097-1113

The current default policy is * → PROMPT_EVERY_REQUEST. This is correct because:

  • For roles with requires_approval = 0, policy_check_with_role() returns POLICY_ALLOW before consulting the policy table.
  • For roles with requires_approval = 1, the policy table prompt still fires as expected.

No change needed here.

5. --allow-all flag — retain as-is

File: src/main.c:3678

The --allow-all flag sets g_prompt_always_allow, which bypasses prompts for algorithm-based verbs (sign, verify, encapsulate, etc.) that don't go through the role system. This is still useful for testing and non-interactive scenarios. No change needed.

Authorization flow after changes

flowchart TD
    A[Client sends request with role name] --> B{Role found?}
    B -- No --> C[Reject: unknown_role]
    B -- Yes --> D{requires_approval?}
    D -- No --> E[Authorize immediately - role-as-password]
    D -- Yes --> F[Check policy table]
    F --> G{Policy match?}
    G -- Allow --> H[Authorize]
    G -- Prompt --> I[Show interactive prompt]
    G -- Deny --> J[Reject: policy_denied]
    I --> K{User choice}
    K -- y --> H
    K -- a/e --> L[Add session grant] --> H
    K -- n --> J

Test impact

Files to modify

File Lines Change
src/main.c 3816 requires_approval = 10
src/main.c 2359-2377 Remove approval prompt, default to 0
src/main.c 2208-2226 Remove approval prompt, default to 0

Summary

Three one-line changes (plus removing two prompt blocks) to make role-as-password the default. The mechanism already exists in the code — it's just not the default.