From 9132db47e2e4e0fca49da2202e5d15655cb41565 Mon Sep 17 00:00:00 2001 From: Jeff Shee Date: Sat, 12 Sep 2026 13:42:20 +0900 Subject: [PATCH] docs(pam): session gate so GNOME asks for the password at first login, face at unlock A face cannot unlock the GNOME keyring, so with pam_visage sufficient in gdm-password the first login after boot succeeds by face and the keyring asks for the password anyway. GNOME uses gdm-password for both the login screen and the lock screen, so PAM cannot separate them by name; what differs is that at unlock the user already owns a session. contrib/pam/visage-has-session is a pam_exec helper that exits 0 when $PAM_USER has a login session and 1 otherwise. Used as auth [success=ignore default=1] pam_exec.so quiet /usr/local/libexec/visage-has-session auth sufficient pam_visage.so first login takes the password path and face stays available for unlock. Only GNOME has been tested; desktops whose locker has its own PAM service just put pam_visage on that service instead. Verified on Fedora 44 / GNOME 50 with a fingerprint reader enrolled: lock-screen unlock by face, password prompt at fresh login, no SELinux denials. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Jeff Shee --- contrib/pam/README.md | 48 ++++++++++++++++++++++++++++++++++ contrib/pam/visage-has-session | 16 ++++++++++++ docs/operations-guide.md | 10 +++++++ 3 files changed, 74 insertions(+) create mode 100644 contrib/pam/README.md create mode 100755 contrib/pam/visage-has-session diff --git a/contrib/pam/README.md b/contrib/pam/README.md new file mode 100644 index 0000000..04be964 --- /dev/null +++ b/contrib/pam/README.md @@ -0,0 +1,48 @@ +# PAM helpers + +## `visage-has-session` — password at first login, face for unlock (GNOME) + +A face cannot unlock the GNOME keyring; that needs the login password. With `pam_visage` +`sufficient` in `gdm-password`, the first login after boot succeeds by face and the keyring +immediately asks for the password anyway. + +GNOME uses the same PAM service, `gdm-password`, for the login screen and the lock screen, +so PAM cannot tell them apart by name. What does differ is that at unlock the user already +owns a login session. This helper checks that via `loginctl`: exit 0 when `$PAM_USER` has a +session of class `user` (unlock, run face auth), exit 1 otherwise (first login, use the +password). Desktops whose lock screen uses its own PAM service do not need it: put +`pam_visage` on the unlock service only. Only GNOME has been tested. + +### Install + +```bash +sudo install -D -m 755 contrib/pam/visage-has-session /usr/local/libexec/visage-has-session +``` + +In `/etc/pam.d/gdm-password`, replace the `pam_visage` line with: + +```text +auth [success=ignore default=1] pam_exec.so quiet /usr/local/libexec/visage-has-session +auth sufficient pam_visage.so +``` + +On success the helper contributes nothing and PAM continues into `pam_visage`; otherwise PAM +skips one module and lands on the password stack. Leave `sudo`, `polkit-1` and other +non-login services as plain `auth sufficient pam_visage.so`. + +### Test + +```bash +PAM_USER=$USER /usr/local/libexec/visage-has-session; echo $? # 0 while logged in +PAM_USER=nobody /usr/local/libexec/visage-has-session; echo $? # 1 +``` + +Then lock the screen and unlock by face; after the next reboot the login screen should ask +for the password. + +### Fingerprint readers + +With `authselect … with-fingerprint`, GDM runs `gdm-password` and `gdm-fingerprint` +concurrently. Keep `pam_visage` in `gdm-password` only; adding it to the fingerprint stack +would run two camera verifies against one device. A fingerprint touch at first login still +bypasses the keyring unlock, as it always has. diff --git a/contrib/pam/visage-has-session b/contrib/pam/visage-has-session new file mode 100755 index 0000000..2e1b91b --- /dev/null +++ b/contrib/pam/visage-has-session @@ -0,0 +1,16 @@ +#!/bin/sh +# pam_exec helper for pam_visage: exit 0 when $PAM_USER already owns a login +# session (this is a screen unlock), exit 1 when they do not (first login). +# +# Face auth cannot unlock the GNOME keyring, which needs the password. So on +# a fresh login we skip pam_visage and let the password prompt run; once a +# session exists the keyring is already open and face unlock is harmless. +# +# Install: install -D -m 755 visage-has-session /usr/local/libexec/visage-has-session +# Then see README.md in this directory for the PAM lines. +[ -n "$PAM_USER" ] || exit 1 +for id in $(loginctl list-sessions --no-legend 2>/dev/null | awk '{print $1}'); do + set -- $(loginctl show-session "$id" -p Name -p Class --value 2>/dev/null) + [ "$1" = "$PAM_USER" ] && [ "$2" = "user" ] && exit 0 +done +exit 1 diff --git a/docs/operations-guide.md b/docs/operations-guide.md index 298e992..a4a6cf2 100644 --- a/docs/operations-guide.md +++ b/docs/operations-guide.md @@ -343,6 +343,16 @@ the threshold to 0.35. --- +## GNOME: keyring and the login screen + +A face cannot unlock the GNOME keyring, and GNOME uses the same PAM service, `gdm-password`, +for login and lock screen. With `pam_visage` there, the first login after boot succeeds by face +and the keyring asks for the password anyway. `contrib/pam/visage-has-session` is a `pam_exec` +gate that skips face when the user has no session yet (first login) and allows it at unlock; +see [`contrib/pam/README.md`](../contrib/pam/README.md). + +--- + ## Suspend and Resume Visage automatically handles suspend/resume via `visage-resume.service`. When the system