Files
gtk-ahfail/readme.md
Asger Geel Weirsøe 5817074f1a
Some checks failed
Test / test (push) Failing after 4m25s
Release / release (push) Failing after 6m37s
release: v0.9.0 pre-release — X11 PAM integration, shadow suppression, macOS cfg guards
- PAM module now correctly fires on failed attempts (must precede auth includes)
- ahfail-display: sprite-sized window replaces full-screen overlay
- ahfail-display: XGrabKeyboard unlock detection replaces process scanning
- ahfail-display: _COMPTON_SHADOW=0 suppresses picom shadow without config changes
- ahfail-display: flock single-instance guard prevents stacking on rapid failures
- X11-specific code guarded behind #[cfg(target_os = "linux")] for macOS builds
- Removed debug logging (dlog/plog) from display and PAM binaries
- README: PAM ordering requirement, supported locker table, roadmap section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 22:01:02 +02:00

256 lines
9.0 KiB
Markdown

# Ah ah ah, you didn't say the magic word
On a failed lock-screen unlock attempt, this spawns a looping animation of **the author's face photoshopped onto Dennis Nedry's body** and plays the "ah ah ah, you didn't say the magic word" clip from Jurassic Park. Each wrong guess adds another sprite at a random screen position. Volume is forced to 100% on the first failure and restored when the screen unlocks.
---
> **Security disclaimer**
>
> This project hooks into PAM and/or your screen locker's plugin API. PAM sits directly in the authentication critical path — a bug in this module could lock you out of your system or, in the worst case, weaken authentication.
>
> The author makes no guarantees about the security or correctness of this software. By installing it you accept that you are modifying a security-sensitive component of your system and take full responsibility for any vulnerabilities or instability that result. **Use at your own discretion.**
---
## Platform support
The integration method differs by display server:
| Platform | Display server | Module loaded | How it works |
|---|---|---|---|
| Linux | **Wayland** | `ahfail-module.so` | Loaded directly by `gtklock` via its module API |
| Linux | **X11** | `libahfail_pam.so` + `ahfail-display` | PAM cleanup hook spawns the display binary after each failed auth |
| macOS | — | `libahfail_pam.so` + `ahfail-display` | Same PAM approach, hooks into the screensaver PAM stack |
The **gtklock module** is Wayland-only — it uses gtklock's internal window API to overlay sprites directly on the lock screen. The **PAM module** works on X11 and macOS by registering a cleanup callback that fires on each failed authentication attempt; it double-forks a standalone display binary (`ahfail-display`) that runs independently of the PAM stack.
---
## Linux (x86\_64) — binary install
Pre-built binaries for both Wayland and X11 are available on the [releases page](https://gitea.weircon.dk/agw/gtk-ahfail/releases). The tarball contains all three files:
| File | Used by |
|---|---|
| `ahfail-module.so` | Wayland — loaded by `gtklock` |
| `libahfail_pam.so` | X11 — loaded by PAM |
| `ahfail-display` | X11 — spawned by the PAM module |
```bash
# Download and extract (replace v0.1.0 with the latest release)
curl -fsSL https://gitea.weircon.dk/agw/gtk-ahfail/releases/download/v0.1.0/ahfail-linux-x86_64.tar.gz \
| sudo tar -xz -C /tmp/ahfail-install
# Wayland (gtklock)
sudo install -Dm755 /tmp/ahfail-install/ahfail-module.so /usr/lib/gtklock/ahfail-module.so
# X11 (PAM module + display binary)
sudo install -Dm755 /tmp/ahfail-install/libahfail_pam.so /usr/lib/ahfail/libahfail_pam.so
sudo install -Dm755 /tmp/ahfail-install/ahfail-display /usr/lib/ahfail/ahfail-display
```
Then follow the [Wayland](#usage-linux---wayland-only-gtklock) or [X11](#usage-linux---x11-i3lock-xscreensaver-etc) configuration below.
---
## Linux compile
### Install dependencies and build from source
```bash
sudo pacman -S meson ninja rust gtk3 gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gtklock
meson setup builddir --prefix=/usr
meson compile -C builddir
sudo meson install -C builddir
```
`--prefix=/usr` matches Arch conventions (same as pacman) and ensures the compiled-in default path for `ahfail-display` matches where it is installed.
Installs:
- `/usr/lib/gtklock/ahfail-module.so` — gtklock module
- `/usr/lib/ahfail/libahfail_pam.so` — PAM module (for X11)
- `/usr/lib/ahfail/ahfail-display` — standalone display binary
---
## Usage Linux - wayland (only gtklock)
```bash
gtklock -m /usr/lib/gtklock/ahfail-module.so
```
### Arguments
Pass module arguments after `--`:
```bash
gtklock -m /usr/lib/gtklock/ahfail-module.so -- --deadzone=860,440,200,200
```
- `--deadzone=X,Y,W,H` — rectangle where sprites will not spawn (keep your password field clear)
- `--audio-uri=URI` — override the default audio clip, e.g. `file:///home/user/custom.mp3`
---
## Usage Linux - X11 (i3lock, xscreensaver, etc.)
The PAM module works with any X11 locker that authenticates via PAM. Supported lockers and their service file names:
| Locker | PAM service file |
|---|---|
| i3lock | `/etc/pam.d/i3lock` |
| i3lock-color | `/etc/pam.d/i3lock-color` |
| betterlockscreen | `/etc/pam.d/betterlockscreen` |
| xscreensaver | `/etc/pam.d/xscreensaver` |
| lightdm | `/etc/pam.d/lightdm` |
Add a line to the relevant file.
**Important:** The `ahfail` line must appear **before** `auth include system-auth` (or any equivalent include). If it comes after, PAM's internal flow control inside `system-auth` (`pam_faillock` with `[default=die]`) means our module is never reached on failed attempts — it only gets called on success.
Your PAM service file should look like this:
**Arch / standard (`--prefix=/usr`):**
```
#%PAM-1.0
auth optional /usr/lib/ahfail/libahfail_pam.so
auth include system-auth
```
**Fedora / RHEL (multilib):**
```
#%PAM-1.0
auth optional /usr/lib64/ahfail/libahfail_pam.so
auth include system-auth
```
**Debian / Ubuntu (multiarch):**
```
#%PAM-1.0
auth optional /usr/lib/x86_64-linux-gnu/ahfail/libahfail_pam.so
auth include system-auth
```
The full path is required — `$(libdir)/ahfail` is not in PAM's default search path.
If the display binary is not at the default location, add `display_path=`:
```
auth optional /usr/lib/ahfail/libahfail_pam.so display_path=/usr/lib/ahfail/ahfail-display
```
---
## macOS
### Homebrew (recommended)
```bash
brew tap agw/ahfail https://gitea.weircon.dk/agw/homebrew-ahfail
brew install ahfail
```
After install, Homebrew prints the PAM line to add to `/etc/pam.d/screensaverui` (macOS 13+) or `/etc/pam.d/screensaver` (macOS 12 and earlier). That one-line edit is the only manual step.
To upgrade: `brew upgrade ahfail`.
### Script install
```bash
git clone https://gitea.weircon.dk/agw/gtk-ahfail.git
cd gtk-ahfail
bash scripts/install-macos.sh
```
Installs Homebrew dependencies, builds from source, copies binaries to `/usr/local/lib/ahfail/`, and patches the screensaver PAM configuration automatically.
### Manual install
```bash
brew install gtk+3 gstreamer gst-plugins-base gst-plugins-good meson ninja
# install Rust via https://rustup.rs if not present
meson setup builddir && meson compile -C builddir
sudo mkdir -p /usr/local/lib/ahfail
sudo cp builddir/libahfail_pam.so builddir/ahfail-display /usr/local/lib/ahfail/
```
Add to `/etc/pam.d/screensaverui` (macOS 13+) or `/etc/pam.d/screensaver` as the **first** `auth` line (before any `auth include` or `auth required` entries):
```
auth optional /usr/local/lib/ahfail/libahfail_pam.so
```
---
## Uninstall
### Linux — X11
```bash
bash scripts/uninstall-linux-x11.sh
```
Removes the `ahfail` line from common PAM service files (`/etc/pam.d/i3lock`, `xscreensaver`, `lightdm`, etc.) and removes the installed binaries. If `builddir` is present it uses `ninja uninstall`; otherwise it removes the known paths manually.
### Linux — Wayland
No PAM config was modified. Just stop passing the module to gtklock and optionally remove the file:
```bash
sudo rm -f /usr/lib/gtklock/ahfail-module.so # --prefix=/usr install
# or
sudo rm -f /usr/local/lib/gtklock/ahfail-module.so
```
### macOS — Homebrew
```bash
sudo sed -i '' '/ahfail/d' /etc/pam.d/screensaverui # or screensaver on macOS 12
brew uninstall ahfail
```
### macOS — manual / script install
```bash
bash scripts/uninstall-macos.sh
```
Removes the `ahfail` line from the screensaver PAM file and deletes `/usr/local/lib/ahfail/`.
---
## Customization
The sprite is the author's face on Nedry's body. To use your own:
1. Replace the sprite frames in `assets/sprites/` with your own PNG sequence.
2. Update `assets/ahfail.gresource.xml` if you add or remove files.
3. Rebuild with Meson.
To replace the audio clip, swap `assets/audio/magic-word.mp3` and rebuild.
---
## Development
```bash
cargo test # unit + integration tests (needs display)
cargo test --test benchmarks -- --nocapture # sprite placement benchmarks
cargo clippy # lint
meson setup builddir && meson compile -C builddir # full build including .so
xvfb-run meson test -C builddir --verbose # Meson tests headless
```
---
## Roadmap
### swaylock (Wayland)
The PAM module can be added to `/etc/pam.d/swaylock` and will fire on each failed attempt — but on Wayland the `ahfail-display` binary cannot draw on top of the lock screen. The Wayland session-lock protocol (`ext-session-lock-v1`) restricts rendering to the locker process itself, so the animation is suppressed by the compositor and only the audio plays.
Full support requires swaylock to expose a plugin API similar to gtklock's. There is an open request for this upstream. Once available, a dedicated swaylock module can be added alongside the existing gtklock one.
### hyprlock (Wayland)
Same situation as swaylock — PAM fires correctly but the display is blocked by the compositor. hyprlock does not currently have a plugin/module API. Full animation support is pending upstream plugin support.