PAM FIDO2 Vault

Hardware-agnostic Linux PAM login and KeePassXC vault auto-unlock powered by FIDO2 (hmac-secret with User Verification / clientPin). Compatible with all standard FIDO2 / CTAP 2.0+ authenticators (YubiKey 5 / Bio Series, FEITIAN BioPass, Thetis, SoloKey 2, Nitrokey 3, etc.).

Security Architecture & Threat Model

  • Hardware Agnostic: Operates on the standard FIDO2 / CTAP 2.0+ protocol via libfido2. Automatically discovers any plugged-in FIDO2 authenticator regardless of vendor (Yubico, Feitian, Thetis, SoloKeys, Nitrokey, etc.).
  • Stolen Laptop Immunity: The FIDO2 credential strictly enforces clientPin (User Verification) in the security key's secure element. If an attacker steals the laptop and boots a live USB recovery disk or resets the Linux user password, they cannot decrypt ~/.fido2-vault.enc. The physical hardware token strictly refuses to release the HMAC secret without physical presence and the hardware-verified PIN.
  • Zero Persistent Disk Footprint: Neither the FIDO2 PIN nor the KeePassXC master key is ever written to persistent storage (NVMe/SSD). Decrypted secrets reside transiently in RAM (/run/user/<uid>/kpxc.key on tmpfs) and are immediately wiped (shred -u) after KeePassXC consumes the key via standard input (--pw-stdin). A 15-second background watchdog guarantees cleanup even if KeePassXC fails to start.
  • Fail-Safe Fallback: If the security key is unplugged or absent, pam_fido2_vault quietly yields (PAM_AUTHINFO_UNAVAIL), cleanly falling back to the standard Linux user password prompt. You will never be locked out.

Directory Layout

pam-fido2-vault/
├── helpers/
│   ├── keepassxc-autounlock.sh             # Autounlock script with safety watchdog
│   └── org.keepassxc.KeePassXC.desktop     # Desktop autostart entry
├── src/
│   ├── crypto_common.c / .h                # HKDF-SHA256 & AES-256-GCM authenticated crypto
│   ├── fido2-vault-setup.c                 # Enrollment CLI and status diagnostic tool
│   ├── pam_fido2_vault.c                   # PAM shared module (auth + session)
│   └── vault_format.h                      # Vault file format definitions
├── .gitignore
├── LICENSE                                 # MIT License
├── Makefile                                # Universal build into build/
├── install.sh                              # Universal distro & service installer
└── README.md

Installation & Setup

1. Build and Install (Requires sudo)

Auto-detects PAM directory (Ubuntu, Debian, Fedora, Arch, openSUSE, RHEL, ARM64) and display manager:

cd /home/steve/src/pam-fido2-vault
sudo ./install.sh

Optional: You can target a specific PAM service, e.g. sudo ./install.sh sddm or sudo ./install.sh lightdm.

2. Enroll FIDO2 Key & Master Password

Run the setup utility as your normal user:

fido2-vault-setup
  1. Verifies KeePassXC binary, helper scripts, and symlinks desktop autostart.
  2. Prompts to confirm or select your .kdbx database location.
  3. Prompts if you use a key file with your database and verifies its path.
  4. Enter your FIDO2 security key PIN.
  5. Enter and confirm your KeePassXC master password.
  6. Touch your FIDO2 key when prompted to register the credential.
  7. Touch your FIDO2 key a second time to verify HMAC derivation and encrypt ~/.fido2-vault.enc.

3. Check System Status Anytime

Run the built-in diagnostic checklist:

fido2-vault-setup --status

Example output:

=========================================================
              PAM FIDO2 Vault Status Check               
=========================================================

  [✓] FIDO2 Key:        Detected (/dev/hidraw0 - Security Key(F829) by Thetis)
  [✓] Enrollment:       Active (/home/steve/.fido2-vault.enc, cred_id=96 bytes)
  [✓] PAM Module:       Installed (/lib/x86_64-linux-gnu/security/pam_fido2_vault.so)
  [✓] PAM Service:      Configured (/etc/pam.d/gdm-password)
  [✓] KeePassXC Binary: Found (/home/steve/.local/bin/keepassxc)
  [✓] Unlock Helper:    Installed (/usr/local/bin/keepassxc-autounlock.sh)
  [✓] Autostart Link:   Active (/home/steve/.config/autostart/org.keepassxc.KeePassXC.desktop)
  [✓] Target Database:  Configured (/home/steve/Documents/steve.kdbx)
  [-] Key File:         None (password only)

=========================================================

How Login Works

  1. At the GDM3 (or SDDM/LightDM) login screen, you are prompted: FIDO2 PIN:
  2. Enter your PIN and press Enter.
  3. The prompt displays: Touch your FIDO2 security key to unlock... and the security key flashes.
  4. Tap the security key once.
  5. Desktop logs in!
  6. pam_fido2_vault writes the decrypted key to /run/user/<uid>/kpxc.key in RAM (tmpfs).
  7. Desktop autostart launches keepassxc-autounlock.sh, which pipes the key into keepassxc --pw-stdin, unlocking your database, and shreds /run/user/<uid>/kpxc.key.

PAM Configuration Deep-Dive (/etc/pam.d/gdm-password)

The automated installer (sudo ./install.sh) inserts two specific hooks into /etc/pam.d/gdm-password (or your display manager's service file):

Exact Configuration

#%PAM-1.0
auth    requisite       pam_nologin.so
auth    required        pam_succeed_if.so user != root quiet_success
auth    sufficient      pam_fido2_vault.so
...
@include common-auth
...
@include common-session
session optional        pam_fido2_vault.so
...

Why This Architecture Works

1. Authentication Phase (auth sufficient pam_fido2_vault.so)

  • Placement: Inserted before @include common-auth (and before any legacy pam_u2f.so rule).
  • sufficient Control Flag:
    • When a FIDO2 key is plugged in and the user has enrolled ~/.fido2-vault.enc, the module prompts for the FIDO2 PIN and cued touch.
    • Upon successful verification, the token returns the hardware-derived HMAC secret. The module decrypts the master password in memory and returns PAM_SUCCESS.
    • Because the flag is sufficient, PAM considers authentication fully satisfied and skips subsequent password prompts.
  • Fail-Safe Fallback:
    • If the FIDO2 key is unplugged or the user is not enrolled, the module immediately returns PAM_AUTHINFO_UNAVAIL without prompting.
    • PAM cleanly falls through to @include common-auth, seamlessly presenting the standard Linux user password prompt.
  • In-Memory Secret Handoff:
    • The decrypted secret is held transiently in PAM transaction memory (pam_set_data) accompanied by a zeroing destructor (crypto_cleanse).
    • On screen unlock (where /run/user/<uid> already exists from the active session), the authentication module writes /run/user/<uid>/kpxc.key directly.

2. Session Phase (session optional pam_fido2_vault.so)

  • Placement: Placed after @include common-session.
  • Systemd Runtime Lifecycle:
    • On a cold boot / fresh graphical login, the user's volatile runtime directory /run/user/<uid> does not exist during the auth phase.
    • pam_systemd.so (inside @include common-session) is responsible for allocating the user session and mounting the per-user tmpfs directory /run/user/<uid>.
    • Placing session optional pam_fido2_vault.so after @include common-session guarantees that /run/user/<uid> is mounted and writable before the module attempts to write the key file.
  • Transient Key Materialization:
    • The session hook retrieves the decrypted secret from the PAM transaction data (pam_get_data), writes it to /run/user/<uid>/kpxc.key with strict 0600 permissions owned by <uid>, and securely zeros PAM memory.
  • optional Control Flag:
    • If the user logged in using fallback password authentication (meaning no FIDO2 secret was decrypted), the session hook exits cleanly without impeding the session.

3. Desktop Autounlock (keepassxc-autounlock.sh)

  • When the graphical desktop starts, XDG Autostart executes /usr/local/bin/keepassxc-autounlock.sh.
  • The helper script:
    1. Checks for /run/user/<uid>/kpxc.key.
    2. Spawns an asynchronous 15-second safety watchdog to ensure the key is shredded even if KeePassXC hangs or encounters an error.
    3. Pipes the key into KeePassXC via standard input:
      keepassxc --pw-stdin [options] "$DB" < "$KEY_FILE"
      
      (If a key file was enrolled during setup, it also automatically passes --keyfile <path>).
    4. Immediately overwrites and removes the key file using shred -u.

Supporting Other Display Managers & Services

The same configuration pattern applies to other PAM service files:

  • SDDM (KDE): /etc/pam.d/sddm
  • LightDM (XFCE/Cinnamon): /etc/pam.d/lightdm
  • Console / TTY: /etc/pam.d/login

You can target any service during installation:

sudo ./install.sh sddm
Description
No description provided
Readme MIT 158 KiB
Languages
C 80.9%
Shell 13.2%
Makefile 5.9%