#!/bin/bash

# omarchy:summary=Set up FIDO2 authentication for sudo and polkit
# omarchy:requires-sudo=true

set -e
set -o pipefail


check_fido2_hardware() {
  tokens=$(fido2-token -L 2>/dev/null)
  if [[ -z $tokens ]]; then
    echo -e "\e[31m\nNo FIDO2 device detected. Please plug it in (you may need to unlock it as well).\e[0m"
    return 1
  fi
  return 0
}

setup_pam_config() {
  # Configure sudo
  if ! grep -q pam_u2f.so /etc/pam.d/sudo; then
    echo "Configuring sudo for FIDO2 authentication..."
    sudo sed -i '1i auth    sufficient pam_u2f.so cue authfile=/etc/fido2/fido2' /etc/pam.d/sudo
  fi

  # Configure polkit
  if [[ -f /etc/pam.d/polkit-1 ]] && ! grep -q 'pam_u2f.so' /etc/pam.d/polkit-1; then
    echo "Configuring polkit for FIDO2 authentication..."
    sudo sed -i '1i auth      sufficient pam_u2f.so cue authfile=/etc/fido2/fido2' /etc/pam.d/polkit-1
  elif [[ ! -f /etc/pam.d/polkit-1 ]]; then
    echo "Creating polkit configuration with FIDO2 authentication..."
    sudo tee /etc/pam.d/polkit-1 >/dev/null <<'EOF'
auth      sufficient pam_u2f.so cue authfile=/etc/fido2/fido2
auth      required pam_unix.so

account   required pam_unix.so
password  required pam_unix.so
session   required pam_unix.so
EOF
  fi
}

echo -e "\e[32mSetting up FIDO2 device for authentication.\n\e[0m"

# Install required packages
echo "Installing required packages..."
omarchy-pkg-add libfido2 pam-u2f

if ! check_fido2_hardware; then
  exit 1
fi

# Create the pamu2fcfg file
authdir=/etc/fido2
authfile=/etc/fido2/fido2

# install -d follows a symlink here and applies the mode and ownership to
# whatever it points at, so the credential would be staged and published inside
# the link target and that directory reopened to root:root 755. This is the
# threat omarchy-remove-security-fido2 already names on its side.
if [[ -L $authdir || ( -e $authdir && ! -d $authdir ) ]]; then
  echo -e "\e[31m\n$authdir is not a FIDO2 configuration directory.\e[0m"
  echo "Run omarchy-remove-security-fido2 first, then set FIDO2 up again."
  exit 1
fi

# -f follows symlinks, so the already-registered check below reads a symlinked
# authfile as a registration and leaves it in place, and is false for a
# directory, so it tries to register over one. Only a regular file is a valid
# pam_u2f authfile.
if [[ -L $authfile || ( -e $authfile && ! -f $authfile ) ]]; then
  echo -e "\e[31m\n$authfile is not a FIDO2 registration file.\e[0m"
  echo "Run omarchy-remove-security-fido2 first, then set FIDO2 up again."
  exit 1
fi

if [[ ! -f $authfile ]]; then
  sudo install -d -m 755 -o root -g root "$authdir"
  echo -e "\e[32m\nLet's setup your device by confirming on the device now.\e[0m"
  echo -e "Touch your FIDO2 key when it lights up...\n"

  # A unique sibling created by root cannot be replaced by another process
  # running as this user. Stream pamu2fcfg into it instead of asking root to
  # reopen a caller-owned path: an observed temporary name could otherwise be
  # replaced with a symlink before the privileged copy. The final rename is
  # atomic, and -T refuses a directory at the destination. Mode 644 keeps the
  # root-owned global authfile readable when pam_u2f uses openasuser; only root
  # can still rewrite it.
  stage=""

  # mktemp's output is an operand for four privileged commands below, one of
  # them an rm. Take only the name this script asked for rather than whatever
  # came back on stdout.
  safe_stage_path() {
    local candidate=$1
    local prefix="$authfile.new."
    local suffix

    [[ $candidate == "$prefix"* ]] || return 1
    suffix=${candidate#"$prefix"}
    [[ $suffix =~ ^[[:alnum:]]{6}$ ]]
  }

  cleanup_stage() {
    local status=$?

    if safe_stage_path "$stage"; then
      sudo rm -f -- "$stage" || true
    fi

    return "$status"
  }

  trap cleanup_stage EXIT
  stage=$(sudo mktemp "$authfile.new.XXXXXX")

  if ! safe_stage_path "$stage" || [[ ! -f $stage || -L $stage ]]; then
    echo -e "\e[31m\nCould not create a safe staging file beside $authfile.\e[0m"
    exit 1
  fi

  if pamu2fcfg | sudo tee "$stage" >/dev/null && [[ -s $stage ]]; then
    sudo chmod 644 "$stage"
    sudo mv -Tf "$stage" "$authfile"
    stage=""
    trap - EXIT
    echo -e "\e[32mFIDO2 device registered successfully!\e[0m"
  else
    echo -e "\e[31m\nFIDO2 registration failed. Please try again.\e[0m"
    exit 1
  fi
else
  echo "FIDO2 device already registered."
fi

# Configure PAM
setup_pam_config

# Test with sudo
echo -e "\nTesting FIDO2 authentication with sudo..."
echo -e "Touch your FIDO2 key when prompted.\n"

if sudo echo "FIDO2 authentication test successful"; then
  echo -e "\e[32m\nPerfect! FIDO2 authentication is now configured.\e[0m"
  echo "You can use your FIDO2 key for sudo and polkit authentication."
else
  echo -e "\e[31m\nVerification failed. You may want to check your configuration.\e[0m"
fi
