#!/bin/sh # patch-lock-pam.sh — make the Caelestia lock screen use a faillock-free PAM # service. Two parts, both idempotent: # # 1. DELIVER the faillock-free `caelestia` service into the directory the lock # actually reads — Quickshell.shellDir + "/assets/pam.d" — i.e. # /etc/xdg/quickshell/caelestia/assets/pam.d/caelestia. (Placing it in # /etc/pam.d/caelestia, which PamContext never reads, makes the lock # fall through to assets/pam.d/passwd — still faillock'd — and reject even # the correct password.) # 2. REPOINT the lock's PamContext at it: config: "passwd" -> "caelestia". # # Runs as root (via the installer, or the pacman hook after every caelestia-shell # upgrade, which restores the shell tree under /etc/xdg — so BOTH the asset file # and the repoint must be re-applied here each time, or an upgrade drops them). # # Symptom (before this fix): the lock screen rejects the CORRECT password; only a # reboot escapes. The stock lock authenticates via PamContext { config: "passwd", # configDirectory: shellDir + "/assets/pam.d" } -> assets/pam.d/passwd, which # calls pam_faillock; a few failed unlocks trip a temporary account lock that # then refuses every attempt (right password included) until /run/faillock clears. # # TARGET (Pam.qml) is overridable for testing the change against a copy; the # asset destination is derived from it (shellDir = TARGET/../../..). set -eu HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) SRC_PAM="$HERE/etc-pam.d-caelestia" # the faillock-free service we ship TARGET=${TARGET:-/etc/xdg/quickshell/caelestia/modules/lock/Pam.qml} [ -f "$TARGET" ] || { echo "caelestia-shell lock not found at $TARGET — nothing to patch"; exit 0; } # shellDir = the caelestia shell root = three levels up from modules/lock/Pam.qml. SHELLDIR=$(CDPATH= cd -- "$(dirname -- "$TARGET")/../.." && pwd) ASSETS_PAMD="$SHELLDIR/assets/pam.d" # --- 1. deliver the faillock-free service where the lock reads it ------------- if [ -f "$SRC_PAM" ]; then install -d -m 0755 "$ASSETS_PAMD" install -m 0644 "$SRC_PAM" "$ASSETS_PAMD/caelestia" echo ":: installed $ASSETS_PAMD/caelestia (faillock-free lock auth)" else echo "WARNING: source PAM service $SRC_PAM missing — cannot deliver the lock service." >&2 fi # --- 2. repoint the lock's PamContext at it ---------------------------------- # Already pointed at our service? if grep -q 'config:[[:space:]]*"caelestia"' "$TARGET"; then echo ":: Pam.qml already points at the caelestia lock service" exit 0 fi # Only act if the stock "passwd" service is referenced. if ! grep -q 'config:[[:space:]]*"passwd"' "$TARGET"; then echo "WARNING: Pam.qml does not reference config: \"passwd\" — upstream shape changed." >&2 echo " Leaving it untouched; review the lock PamContext for the new" >&2 echo " service name and update the regex in $(basename "$0")." >&2 exit 0 fi cp -n "$TARGET" "$TARGET.pre-nosignal" 2>/dev/null || true # Repoint only the PAM service name; keep everything else as-is. sed -i 's/config:\([[:space:]]*\)"passwd"/config:\1"caelestia"/g' "$TARGET" if grep -q 'config:[[:space:]]*"caelestia"' "$TARGET"; then echo ":: patched $TARGET (lock now uses the faillock-free caelestia PAM service)" else echo "WARNING: patch did not apply — Pam.qml left untouched." >&2 exit 0 fi