fix(hyprlua): support howdy 2.x in enroll-biometrics

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 <noreply@anthropic.com>
main
Amir Alexander Abdelbaki 2026-06-13 22:13:26 +02:00
parent dc3ffb68b7
commit 88f3b72343
1 changed files with 54 additions and 10 deletions

View File

@ -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"
}
@ -363,13 +411,9 @@ pam_remove_hyprlock() {
pam_setup() {
howdy_require || return
pam_python_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
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.<timestamp>.\n\nContinue?" 15 64 || return