z-auth/z-auth.sh

178 lines
4.4 KiB
Bash
Executable file

#!/bin/bash
## Silent errors
exec 2>/dev/null
PINCODE="2606"
# Detect the target user (default to current user or UID 1000 user if running as root)
get_target_user() {
if [ -n "$PAM_USER" ]; then
echo "$PAM_USER"
elif [ -n "$SUDO_USER" ]; then
echo "$SUDO_USER"
else
echo "$USER"
fi
}
# Helper to find the correct Xauthority file
get_xauthority() {
local target_user="$1"
local target_uid
target_uid=$(id -u "$target_user")
# Common locations for Xauthority
local candidates=(
"/run/user/$target_uid/gdm/Xauthority"
"/run/user/$target_uid/.mutter-Xwaylandauth.*"
"/home/$target_user/.Xauthority"
)
for pattern in "${candidates[@]}"; do
# Expand glob pattern
for file in $pattern; do
if [ -r "$file" ]; then
echo "$file"
return 0
fi
done
done
return 1
}
# Identify a usable TTY device
identify_tty() {
# 1. Try standard /dev/tty (standard controlling terminal)
# Use subshell to test opening it without exiting script on failure
if ( : < /dev/tty ) 2>/dev/null; then
echo "/dev/tty"
return 0
fi
# 2. Try PAM_TTY if set (provided by PAM module)
if [ -n "$PAM_TTY" ]; then
# Use simple pattern matching
case "$PAM_TTY" in
/dev/*)
if [ -c "$PAM_TTY" ]; then
echo "$PAM_TTY"
return 0
fi
;;
ssh|:*)
# Not a char device (X11 or SSH)
return 1
;;
*)
# Potentially a device name without /dev/ (e.g. tty1)
if [ -c "/dev/$PAM_TTY" ]; then
echo "/dev/$PAM_TTY"
return 0
fi
;;
esac
fi
return 1
}
verify_pin() {
local input="$1"
if [ "$input" = "$PINCODE" ]; then
exit 0
else
return 1
fi
}
z_auth_gui() {
local target_user
target_user=$(get_target_user)
# Fallback/Guess display if needed
if [ -z "$DISPLAY" ]; then
export DISPLAY=:0
fi
# Try to find authority file
local auth_file
auth_file=$(get_xauthority "$target_user")
if [ -n "$auth_file" ]; then
export XAUTHORITY="$auth_file"
fi
local input_pincode
input_pincode=$(sudo -u "$target_user" zenity --entry \
--title="Security Check" \
--text="Enter Sudo PIN for $target_user:" \
--hide-text \
--width=300 2>/dev/null)
# If zenity failed/cancelled, return 1 to allow fallback
# DO NOT exit 1 here, otherwise we kill the script before trying TTY
if [ $? -ne 0 ]; then
return 1
fi
if verify_pin "$input_pincode"; then
exit 0
else
sudo -u "$target_user" zenity --error --text="Wrong PIN!" --no-wrap 2>/dev/null
exit 1
fi
}
z_auth_no_gui() {
local tty_device="$1"
if [ -z "$tty_device" ] || [ ! -c "$tty_device" ]; then
# Check failed, but silenced now
exit 1
fi
local input_pincode
# Use the identified TTY device for input and output
echo -n "Enter Security PIN: " > "$tty_device"
read -s input_pincode < "$tty_device"
echo > "$tty_device"
if verify_pin "$input_pincode"; then
exit 0
else
echo "Incorrect PIN." > "$tty_device"
exit 1
fi
}
main_function() {
# Determine where we are
local identified_tty
identified_tty=$(identify_tty) # e.g. /dev/tty3 or /dev/pts/1
# Logic:
# 1. If we are on a REAL console (/dev/tty1...tty6), FORCE TTY mode.
# Why? Because forcing GUI (DISPLAY=:0) on TTY3 will spawn a window
# on TTY1/2 (where X is), which is invisible to the user on TTY3, causing a hang.
if [[ "$identified_tty" =~ /dev/tty[0-9]+ ]]; then
z_auth_no_gui "$identified_tty"
# If it returns, it passed validation
return
fi
# 2. If we are NOT on a real console (e.g. /dev/pts/* or no TTY), try GUI first.
# This covers GNOME Terminal, SSH X11 forwarding, etc.
# If GUI fails (e.g. headless SSH), fallback to TTY.
z_auth_gui
# 3. GUI failed or returned 1? Fallback to TTY if we have one.
if [ -n "$identified_tty" ]; then
z_auth_no_gui "$identified_tty"
else
# No GUI, no TTY -> Fail
exit 1
fi
}
main_function