Skip to content

fix: run lockscreen fingerprint and password as parallel PAM conversations - #239

Closed
natxobsd wants to merge 1 commit into
Ryoku-dev:mainfrom
natxobsd:fix/lock-parallel-pam
Closed

natxobsd wants to merge 1 commit into
Ryoku-dev:mainfrom
natxobsd:fix/lock-parallel-pam

Conversation

@natxobsd

Copy link
Copy Markdown
Contributor

What's wrong

The in-session lock screen supports fingerprint unlock (added in #48/#52 via pam_fprintd_grosshack.so), but the PAM service and the shim run one single PamContext for both the sensor and the typed password. The PAM stack is serialised by design, so both methods end up fighting over the same conversation:

  1. Touch stops working after ~30 s. pam_fprintd.so's default scan timeout is 30 s. When it expires the conversation falls through to pam_unix, which opens a password prompt. The conversation is now alive but waiting for a typed key, so PamContext.active stays true and the shim's re-arm logic (rearmTimer → armFingerprint()) never fires. Touching the sensor does nothing until you type the password once (which ends the conversation and lets the next lock re-arm).

  2. A password typed during the scan is swallowed for up to 30 s. With no prompt open yet, login() stashes the key in pendingPassword and only feeds it when PAM asks — i.e. when the scan finally times out. From the user's point of view the unlock hangs, then the screen whites out late (a stuck stash that auto-submits).

Why

From the pam_fprintd(8) LIMITATIONS section:

The PAM stack is serialised by design, and parallel authentication methods need to run in separate conversations, "the way multiple authentication methods are made available to users of gdm".

A single conversation cannot host a scan that lasts the whole lock and a password prompt that must be pending from t=0.

The fix

Split into two PamContexts started at lock time, first Success wins and aborts the other (guarded against double-fire):

Service Method Behaviour
assets/pam/ryoku-lock fingerprint stock pam_fprintd.so timeout=-1 — scan lives for the whole lock; max-tries (3) still ends it on misreads, shim re-arms after 1 s
assets/pam/ryoku-lock-pw (new) password pam_unix.so nullok — prompt pending from t=0, a typed key answers immediately, no stash

A wrong password shakes the field and re-prompts without disturbing the sensor conversation — the scan keeps running while the user retypes. The debug flags used for diagnosis are already stripped.

README.ryoku.md is updated: the shim's divergence from upstream qylock is no longer "in one place".

How it was tested

Arch, Ryoku v0.63.1-beta.19 (e76a32d), fprintd 1.94.5, Synaptics 06cb:00df reader, ThinkPad. Journal evidence with debug on both services (flags since removed):

Case Timeline Result
Touch right after locking 19:11:41 VerifyStart → 19:11:44 verify-match unlocks on touch (3 s)
Touch after ~60 s idle 19:12:02 VerifyStart → 19:13:00 verify-match unlocks on touch after 58 s — scan was still alive (Waiting for 9223372036854 s = timeout=-1), the case that used to fail
Password typed immediately 19:13:11 pam_unix(ryoku-lock-pw:auth): username obtained prompt answered in the same second as the lock — no 30 s stash
Wrong password — shakes the field, scan keeps running, retry works

Before this change the same three tests produced: case 1 ✅, case 2 ❌ (dead sensor), case 3 ⚠️ (auto-submit after 30 s). qmllint is clean on the patched shim.

The same root cause family as #237 (the polkit agent blocking on a pending prompt) — that one is system config, this one is the vendored lockscreen, hence the separate PR.

…tions

A single PAM conversation cannot host both methods: the stack is
serialised by design (pam_fprintd(8) LIMITATIONS), so the password
prompt starves the sensor. With the stock module the default 30s scan
timeout falls through to pam_unix, the conversation stays alive waiting
for a typed key, the QML sees pam.active and never re-arms the scan
(touch stops working), and a password typed during the scan is stashed
until the prompt finally arrives (up to 30s later).

Split into two PamContexts started at arm time:

- ryoku-lock: fingerprint only, pam_fprintd.so timeout=-1, so the scan
  lives for the whole lock; max-tries (3) still ends it on misreads and
  the shim re-arms after 1s
- ryoku-lock-pw: password only, prompt pending from lock time, so a
  typed key answers immediately with no stash

First Success unlocks and aborts the other (guarded against double
fire); a wrong password shakes the field and re-prompts without
disturbing the sensor conversation.

Verified on v0.63.1-beta.19 with debug PAM logs (since removed):
touch at 3s, touch still matching after 58s of scanning, password
obtained in the same second as the lock. qmllint clean.
@neur0map

Copy link
Copy Markdown
Collaborator

landed on unstable-dev as e2fe732 with one small addition (the sensor-win flag no longer rides the typed-key heuristic, since the fingerprint conversation never prompts). your analysis was spot on; thanks for the careful writeup and the PAM trace. it'll reach main with the next fast-forward.

@neur0map neur0map closed this Sep 22, 2026
@natxobsd

Copy link
Copy Markdown
Contributor Author

thanks for landing it. good call on the sensor-win flag — with the conversations split the typed-key heuristic had nothing left to detect, so the unconditional read is strictly better. picked that up locally already; will ride the next fast-forward to main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants