From 88f3b7234381c640c82973201364080317a96991 Mon Sep 17 00:00:00 2001 From: The_miro Date: Sat, 13 Jun 2026 22:13:26 +0200 Subject: [PATCH] fix(hyprlua): support howdy 2.x in enroll-biometrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit howdy 2.x installs its CLI under root-only /usr/lib/security/howdy/ with /usr/bin/howdy symlinked into it, so `command -v howdy` reports "not found" for a normal user — the script wrongly thought howdy wasn't installed and tried to reinstall it. Detect via the symlink and pacman -Qq instead; all ops already run through `sudo howdy`. This version also has no pam_howdy.so — it authenticates via pam_python.so loading /usr/lib/security/howdy/pam.py. Switch the emitted PAM block to that module and add pam_python_require() to install the AUR `pam-python` dependency, replacing the dead pam_howdy.so existence check. Co-Authored-By: Claude Opus 4.8 --- .../hyprlua/scripts/enroll-biometrics.sh | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/desktopenvs/hyprlua/scripts/enroll-biometrics.sh b/desktopenvs/hyprlua/scripts/enroll-biometrics.sh index d9ba868..b744974 100755 --- a/desktopenvs/hyprlua/scripts/enroll-biometrics.sh +++ b/desktopenvs/hyprlua/scripts/enroll-biometrics.sh @@ -14,7 +14,10 @@ PYTHON_DETECT="$SCRIPT_DIR/python/presence_detect.py" # Both factors required, with the normal password stack kept as a fallback so a # dead camera or absent key can never lock you out. PAM_TARGETS=(/etc/pam.d/sudo /etc/pam.d/hyprlock /etc/pam.d/login) -PAM_HOWDY_SO="/usr/lib/security/pam_howdy.so" +# howdy 2.x has no pam_howdy.so — it authenticates through pam_python.so loading +# its own entrypoint script. pam_python.so ships in the AUR package `pam-python`. +PAM_PYTHON_SO="/usr/lib/security/pam_python.so" +HOWDY_PAM_PY="/usr/lib/security/howdy/pam.py" PAM_U2F_SO="/usr/lib/security/pam_u2f.so" U2F_MAP="/etc/u2f_mappings" PAM_MARK_BEGIN="# enroll-biometrics:begin howdy+u2f 2fa" @@ -143,7 +146,16 @@ presence_test_camera() { } # ── Howdy helpers ───────────────────────────────────────────────────────────── -howdy_installed() { command -v howdy &>/dev/null; } +# Detection must survive howdy 2.x's layout: its CLI lives under root-only +# /usr/lib/security/howdy/ with /usr/bin/howdy symlinked into it, so a normal +# user can neither resolve nor run the link — `command -v howdy` reports "not +# found" even when it is installed. Every op here invokes it via `sudo howdy` +# anyway, so fall back to the symlink itself and the package DB. +howdy_installed() { + command -v howdy &>/dev/null && return 0 + [[ -L /usr/bin/howdy || -e /usr/bin/howdy ]] && return 0 + command -v pacman &>/dev/null && pacman -Qq howdy &>/dev/null +} howdy_require() { howdy_installed && return 0 @@ -243,6 +255,42 @@ howdy_test() { fi } +# ── howdy PAM module (pam_python.so) ────────────────────────────────────────── +# howdy's PAM entrypoint is a Python script loaded by pam_python.so. The module +# is /usr/lib/security/pam_python.so directly (world-traversable, unlike howdy's +# own root-only dir), so a plain existence test is reliable here; pacman -Qq is +# the fallback in case it lands elsewhere. +pam_python_installed() { + [[ -e "$PAM_PYTHON_SO" ]] && return 0 + command -v pacman &>/dev/null && pacman -Qq pam-python &>/dev/null +} + +pam_python_require() { + pam_python_installed && return 0 + + # pam-python is AUR-only, so it needs an AUR helper just like howdy. + local helper + for h in yay paru; do + command -v "$h" &>/dev/null && { helper="$h"; break; } + done + if [[ -z "$helper" ]]; then + msg "No AUR Helper" \ + "howdy's PAM module needs the AUR package 'pam-python', which requires an AUR helper (yay or paru).\n\nInstall yay first, then re-run." \ + 11 62 + return 1 + fi + + dialog --backtitle "$BACKTITLE" --title " pam-python Needed " \ + --yesno "\nhowdy authenticates via pam_python.so, provided by the AUR package 'pam-python'.\n\nInstall it now via ${helper}?" 10 62 || return 1 + clear + printf "\nInstalling pam-python via %s...\n\n" "$helper" + "$helper" -S --noconfirm --needed pam-python + pam_python_installed || { + msg "Install Failed" "pam-python installation failed.\nInstall it manually: ${helper} -S pam-python" 8 58 + return 1 + } +} + # ── FIDO U2F helpers ──────────────────────────────────────────────────────────── fido_installed() { [[ -e "$PAM_U2F_SO" ]] && command -v pamu2fcfg &>/dev/null; } @@ -291,7 +339,7 @@ fido_register() { # existing (password) auth lines below. pam_emit_block() { printf '%s\n' "$PAM_MARK_BEGIN" - printf 'auth [success=ignore default=1] pam_howdy.so\n' + printf 'auth [success=ignore default=1] pam_python.so %s\n' "$HOWDY_PAM_PY" printf 'auth sufficient pam_u2f.so authfile=%s cue\n' "$U2F_MAP" printf '%s\n' "$PAM_MARK_END" } @@ -362,13 +410,9 @@ pam_remove_hyprlock() { } pam_setup() { - howdy_require || return - fido_require || return - - if [[ ! -e "$PAM_HOWDY_SO" ]]; then - msg "Missing PAM Module" "pam_howdy.so not found at:\n$PAM_HOWDY_SO\n\nReinstall howdy and try again." 10 60 - return - fi + howdy_require || return + pam_python_require || return + fido_require || return dialog --backtitle "$BACKTITLE" --title " Set Up PAM 2FA " --yesno \ "\nThis will require BOTH face (howdy) and a FIDO key for:\n\n ${PAM_TARGETS[*]}\n\nYour password still works if a factor is unavailable.\nBackups of each file are saved as *.bak..\n\nContinue?" 15 64 || return