#!/bin/sh
# Install pam_pwquality on a Debian 13 (trixie) host and put it in the password
# stack, enforcing rather than warning.
#
# This imposes no password rule of its own. The rules are separate components, each
# owning its own settings, that depend on this one:
#   debian-13-pwquality-minlen, -no-complexity, -dictionary, -predictability
#
# Stock Debian stacks only "pam_unix.so obscure yescrypt" with no minlen, so the
# effective floor is pam_unix's built-in 6 characters, and libpam-pwquality is not
# installed at all.
#
# Usage: apply-pwquality-base.sh
# Idempotent: safe to re-run.

set -eu

[ "$(id -u)" -eq 0 ] || { echo "error: must run as root" >&2; exit 1; }

BASE_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
DROPIN=40-ism-base.conf

pam_securedir() {
	unix=$(dpkg -L "libpam-modules:$(dpkg --print-architecture)" 2>/dev/null |
		grep '/security/pam_unix\.so$' | head -n 1)
	[ -n "$unix" ] && [ -f "$unix" ] || return 1
	dirname "$unix"
}

unresolved_modules() {
	grep -v '^[[:space:]]*#' /etc/pam.d/common-password |
		grep -oE '[^][:space:]]+\.so([[:space:]]|$)' | tr -d ' \t' |
		while IFS= read -r mod; do
			case "$mod" in
				/*) [ -f "$mod" ] || echo "$mod" ;;
				*)  [ -f "$SECDIR/$mod" ] || echo "$mod" ;;
			esac
		done
}

SECDIR=$(pam_securedir) || { echo "error: cannot locate the PAM module directory" >&2; exit 1; }
MISSING=$(unresolved_modules)
[ -z "$MISSING" ] || {
	echo "error: /etc/pam.d/common-password already names modules PAM cannot find:" >&2
	printf '         %s\n' $MISSING >&2
	exit 1
}

# cracklib-runtime carries the dictionary debian-13-pwquality-dictionary needs;
# libpwquality-tools provides pwmake and pwscore, which satisfy IA-5(1)(g) and drive
# every verifier in the family.
echo "==> installing packages"
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
	libpam-pwquality libpwquality-tools cracklib-runtime

echo "==> installing $DROPIN"
install -d -m 0755 /etc/security/pwquality.conf.d
install -m 0644 "$BASE_DIR/config/pwquality.conf.d/$DROPIN" "/etc/security/pwquality.conf.d/$DROPIN"

# libpam-pwquality ships /usr/share/pam-configs/pwquality with "Default: yes" at
# priority 1024, ahead of pam_unix at 256. --package keeps this non-interactive.
echo "==> enabling pam_pwquality in the PAM stack"
pam-auth-update --package --enable pwquality

MISSING=$(unresolved_modules)
[ -z "$MISSING" ] || {
	pam-auth-update --package --remove pwquality
	echo "error: after enabling pwquality, PAM cannot find:" >&2
	printf '         %s\n' $MISSING >&2
	exit 1
}

echo "==> applied. Verify with: scripts/verify-pwquality-base.sh"
