diff --git a/bin/config_system.ini b/bin/config_system.ini new file mode 100644 index 0000000..5e6fbb8 --- /dev/null +++ b/bin/config_system.ini @@ -0,0 +1,6 @@ +[USERCONFIG] +remove_sudo_timestamp_when_locking = true + +[HOSTCONFIG] +logfile = log.log +loglevel = 10 diff --git a/bin/config_user.ini b/bin/config_user.ini new file mode 100644 index 0000000..d556488 --- /dev/null +++ b/bin/config_user.ini @@ -0,0 +1,3 @@ +[USERCONFIG] +yubikey_serial = 12345678 +remove_sudo_timestamp_when_locking = true diff --git a/bin/install.sh b/bin/install.sh new file mode 100755 index 0000000..34d8079 --- /dev/null +++ b/bin/install.sh @@ -0,0 +1,157 @@ +#! /bin/bash + +install_dir='/opt/yubilock/' +logging_dir='/var/log/yubilock/' + +script_dir="$(dirname $(readlink -f $0))" +# exit when any command fails +set -e + +# Make sure running as root +if [ `id -u` -ne 0 ]; then + echo 'Please run as root' + exit 1 +fi + +# Ask for user parameters +echo "This installer is meant to install the yubilock service for one user. Please specifiy for wich user you want to install xscreensaver-yubilock" +read -p 'Username: ' username +userid=`id -u "$username" 2>/dev/null` || ( echo "$username is not a user on this system" && exit 1 ) +[ "$userid" -lt 1000 ] && echo "User $username seems to be a systemuser (uid: $userid). Please specify a normal user." && exit 1 + +echo "Allowed yubikey serials can be set systemwide in ${install_dir}config.ini, or per user in \$HOME/.yubilock. Do you wish to add one or more for $username now?" +read -p "Add Yubikey serial? (Y/n) " add_serial +[ -z "$add_serial" ] && add_serial='yes' # if no input, assume yes +case ${add_serial:0:1} in + y|Y|1 ) + add_serial='yes';; + * ) + add_serial='no';; +esac +if [ "$add_serial" = 'yes' ]; then + if ! ykman -v >/dev/null 2>&1 ; then + echo "yubikey-manager doesn't seem to be installed. Do you want to install it? ('no' means you'll have to add your yubikey serial manually later)" + read -p "Install yubikey-manager? (Y/n) " install_ykman + [ -z "$install_ykman" ] && install_ykman='yes' # if no input, assume yes + case ${install_ykman:0:1} in + y|Y|1 ) + apt-get install -y yubikey-manager;; + * ) + break 3;; + esac + fi + echo "Please make sure your yubikey(s) are plugged in. Then press any key to continue" + read -n 1 -s -r + serials=`ykman list | sed -e 's#.*:\ \(\)#\1#' | tr '\n' ','` # List all keys, get the serials, and comma separate them + serials="${serials%?}" # Remove trailing comma + echo "The following serial(s) will be added to your config file: $serials" +fi + + + +echo "Do you want the daemon to be started by systemd? (you'll have to start it manually every login session if you choose no)" +read -p "Use Systemd? (Y/n) " use_systemd +[ -z "$use_systemd" ] && use_systemd='yes' # if no input, assume yes +case ${use_systemd:0:1} in + y|Y|1 ) + use_systemd='yes';; + * ) + use_systemd='no';; +esac + + + +echo "== Making sure python3 and virtualenv are installed ==" +python3 --version || apt-get install -y python3 +python3 -m venv -h >/dev/null 2>&1 || apt-get install -y python3-venv + + + +echo "== Create yubilock group ==" +addgroup --system yubilock +echo "== Add $username to yubilock group ==" +usermod -a -G yubilock "$username" + + + +echo "== Create virualenv ==" +[ -f "$install_dir/venv/bin/activate" ] || python3 -m venv "$install_dir/venv" +. "$install_dir/venv/bin/activate" +pip install setuptools wheel +pip install -r "$script_dir/requirements.txt" + + + +echo "== Copy over application files ==" +cp "$script_dir/bin/xscreensaver_yubilock.py" "$install_dir" +cp "$script_dir/bin/uninstall.sh" "$install_dir" +cp "$script_dir/bin/kill_screensaver_graphic_program.sh" "$install_dir" +cp "$script_dir/bin/config_system.ini" "$install_dir/config.ini" + +chown -R root:yubilock "$install_dir" +chmod 771 "$install_dir" + + + +# Add yubikey serials to config +if [ -n "$serials" ]; then + homedir=`eval echo ~"$username"` + echo "Homedir: $homedir" + [ -f "$homedir/.yubilock" ] || ( cp "$script_dir/bin/config_user.ini" "$homedir/.yubilock" && chown "$username:$username" "$homedir/.yubilock") + sed -i "s+^yubikey_serial.*+yubikey_serial\ =\ $serials+g" "$homedir/.yubilock" + echo "Add yubikey serial(s) to $homedir/.yubilock" +fi + + + +echo "== Create logging directory ==" +mkdir -p "$logging_dir" +chown --from=root:root root:yubilock "$logging_dir" +chmod 775 "$logging_dir" +sed -i "s+^logfile\ =.*+logfile\ =\ ${logging_dir}daemon.log+g" "$install_dir/config.ini" + + + +echo "== Fix udev usb rights for yubilock group ==" +cp "$script_dir/debian/91-usbftdi.rules" '/etc/udev/rules.d/' +chown root:root '/etc/udev/rules.d/91-usbftdi.rules' +udevadm control --reload-rules + + +if [ "$use_systemd" = 'yes' ]; then + echo "== Enable as systemd service ==" + mkdir -p "/home/$username/.config/systemd/user" + cp "$script_dir/debian/yubilock.service" "/home/$username/.config/systemd/user" + sed -i "s+^ExecStart=.*+ExecStart=${install_dir}venv/bin/python ${install_dir}xscreensaver_yubilock.py -v+g" "/home/$username/.config/systemd/user/yubilock.service" + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user daemon-reload' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user enable yubilock.service' + # su is used for systemctl user units because systemctl matches executing uid to unit owner uid. See: + # https://unix.stackexchange.com/questions/483948/inspect-unit-status-for-user-units-with-systemctl-as-root/485063#485063 +else + # Make sure service is removed if previously installed + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user stop yubilock.service >/dev/null 2>&1' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user disable yubilock.service >/dev/null 2>&1' + rm "/home/$username/.config/systemd/user/yubilock.service" >/dev/null 2>&1 + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user daemon-reload' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user reset-failed' +fi + +echo "== xscreensaver-yubilock is installed! ==" +echo "== to enable yubilock, please restart your device == + +exit 0 +# Due to loginctl not updating user groups, the user has to restart before the service can be started. +if [ "$use_systemd" = 'yes' ]; then + echo "Do you wish to start the daemon now? WARNING: If the specified yubikey is not plugged in, your machine will lock. Alternatively, you can start the service using 'sudo systemctl start yubilock.service' or wait for next login." + read -p "Start daemon? (y/N) " start_daemon + [ -z "$start_daemon" ] && start_daemon='no' # if no input, assume no + case ${start_daemon:0:1} in + n|N|0 ) + ;; + * ) + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user start yubilock.service';; + esac +fi + +exit 0 +#(Uninstall script) diff --git a/bin/kill_screensaver_graphic_program.sh b/bin/kill_screensaver_graphic_program.sh new file mode 100755 index 0000000..08194c5 --- /dev/null +++ b/bin/kill_screensaver_graphic_program.sh @@ -0,0 +1,41 @@ +#! /bin/bash + +function help () { + echo "Usage: $0 [OPTIONS] " + echo "Cleans up xscreensaver graphic programs left behind after killing xscreensaver itself." + echo " -u specify a username for which to kill xscreensaver artifacts" + echo " -h display this output" + exit 1 +} + +while getopts ':u:' opt ; do + case "$opt" in + u) + username="${OPTARG}" + userid=`id -u "$username" 2>/dev/null` || { echo "$username is not a user on this system" && exit 1 ;} + [ "$userid" -lt 1000 ] && echo "User $username seems to be a systemuser (uid: $userid). Please specify a normal user." && exit 1 + ;; + h) help ;; + :) + echo "$0: Must supply an argument to -$OPTARG." >&2 + exit 1 + ;; + ?) + echo "Invalid option: -${OPTARG}." + exit 2 + ;; + esac +done + +username="${username:-$USER}" +homedir=`eval echo "~$username"` +selected_graphic_nr=`grep selected "$homedir/.xscreensaver" | cut -f 2` +regex='/\\n\\/' +graphic_process_name=`cat "$homedir/.xscreensaver" | awk "$regex && ++n == "$selected_graphic_nr" {getline; print; exit}" | cut -f 5 | cut -d ' ' -f 1 ` +echo "graphic_process_name: $graphic_process_name" +[ -z "$graphic_process_name" ] && { echo Could not find selected graphic application && exit 1; } +graphic_processes=`ps -U "$username" | grep "$graphic_process_name" | awk '{$1=$1};1' | cut -d ' ' -f 1 | tr '\n' ' ' ` || { echo "No screensaver graphic processes were found for $username" && exit 0; } +echo "graphic_processes: $graphic_processes" +[ -z "$graphic_processes" ] && { echo "No xscreensaver graphic processes seem to be running for $username" && exit 0; } +process_count=`echo $graphic_processes | wc -w` +kill $graphic_processes && echo "killed $process_count screensaver graphic processes for $username" diff --git a/uninstall.sh b/bin/uninstall.sh similarity index 100% rename from uninstall.sh rename to bin/uninstall.sh diff --git a/xscreensaver_yubilock.py b/bin/xscreensaver_yubilock.py similarity index 95% rename from xscreensaver_yubilock.py rename to bin/xscreensaver_yubilock.py index d119de5..9935fbb 100755 --- a/xscreensaver_yubilock.py +++ b/bin/xscreensaver_yubilock.py @@ -18,9 +18,10 @@ from logzero import logger from usb.core import USBError script_dir = os.path.dirname(os.path.realpath(__file__)) +home_dir = os.path.expanduser("~") config = ConfigParser() -config.read(f"{script_dir}/config.ini") +config.read([f"{script_dir}/config.ini", f"{home_dir}/.yubilock"]) yubikey_serials = config["USERCONFIG"]["yubikey_serial"].split(',') # Convert stringlist to intlist @@ -50,7 +51,7 @@ def execute(command: str, shell_on: bool = False, background: bool = False): def lock_screen(): if args.dummy : return - if config.getboolean('HOSTCONFIG', 'remove_sudo_timestamp_when_locking', fallback=True): + if config.getboolean('USERCONFIG', 'remove_sudo_timestamp_when_locking', fallback=True): execute('sudo -K', shell_on=True) execute('DISPLAY=:0 xscreensaver-command -lock', shell_on=True) return @@ -64,6 +65,7 @@ def unlock_screen(): if xscreensaver_pid != 'null': execute('kill %s' % xscreensaver_pid, shell_on=True) + execute(f"{script_dir}/kill_screensaver_graphic_program.sh", shell_on=True) # restart xscreensaver process execute('DISPLAY=:0 xscreensaver -no-splash&', shell_on=True, background = True) @@ -149,6 +151,7 @@ def get_hid_event_monitor(): if __name__ == "__main__": args = get_args() + execute('id > /tmp/id.txt &', shell_on=True, background = True) setup_logger(config.get("HOSTCONFIG", "logfile", fallback="log.log")) diff --git a/config_example.ini b/config_example.ini index 2f0c6d1..700e5a8 100644 --- a/config_example.ini +++ b/config_example.ini @@ -1,8 +1,8 @@ # Make a copy of this file, rename it to config.ini, and replace the yubikey serial [USERCONFIG] yubikey_serial = 12345678 +remove_sudo_timestamp_when_locking = true [HOSTCONFIG] logfile = log.log loglevel = 10 -remove_sudo_timestamp_when_locking = true diff --git a/debian/yubilock.service b/debian/yubilock.service index b8d1a07..dda3204 100644 --- a/debian/yubilock.service +++ b/debian/yubilock.service @@ -1,21 +1,13 @@ [Unit] Description=Yubikey activated xscreensaver locker/unlocker -After=syslog.target multi-user.target lightdm.service -Requires=lightdm.service #Requires=syslog.socket -#Documentation=man:rsyslogd(8) -#Documentation=https://www.rsyslog.com/doc/ +Documentation=https://git.sciuro.org/Burathar/xscreensaver-yubilock [Service] -#Type=simple +Type=simple ExecStart=/opt/yublilock/venv/bin/python /opt/yubilock/xscreensaver_yubilock.py -v -User=yubilock #StandardOutput=null -#Restart=on-failure - -# Increase the default a bit in order to allow many simultaneous -# files to be monitored, we might need a lot of fds. -#LimitNOFILE=16384 +Restart=on-failure [Install] -WantedBy=multi-user.target +WantedBy=default.target diff --git a/install.sh b/install.sh index b9e90d7..34d8079 100755 --- a/install.sh +++ b/install.sh @@ -19,7 +19,7 @@ read -p 'Username: ' username userid=`id -u "$username" 2>/dev/null` || ( echo "$username is not a user on this system" && exit 1 ) [ "$userid" -lt 1000 ] && echo "User $username seems to be a systemuser (uid: $userid). Please specify a normal user." && exit 1 -echo "Allowed yubikey serials can be set in {$install_dir}config.ini. Do you wish to add one or more automaticaly now?" +echo "Allowed yubikey serials can be set systemwide in ${install_dir}config.ini, or per user in \$HOME/.yubilock. Do you wish to add one or more for $username now?" read -p "Add Yubikey serial? (Y/n) " add_serial [ -z "$add_serial" ] && add_serial='yes' # if no input, assume yes case ${add_serial:0:1} in @@ -30,7 +30,7 @@ case ${add_serial:0:1} in esac if [ "$add_serial" = 'yes' ]; then if ! ykman -v >/dev/null 2>&1 ; then - echo "yubikey-manager doesn't seem to be installed. Do you want to install it? ('no' means you'll have to add your yubikey serial manually later" + echo "yubikey-manager doesn't seem to be installed. Do you want to install it? ('no' means you'll have to add your yubikey serial manually later)" read -p "Install yubikey-manager? (Y/n) " install_ykman [ -z "$install_ykman" ] && install_ykman='yes' # if no input, assume yes case ${install_ykman:0:1} in @@ -61,77 +61,86 @@ esac -echo "Create yubilock user" -adduser --system --home "$install_dir" --shell "/usr/sbin/nologin" --group --gecos "xscreensaver yubilock daemon" -q 'yubilock' - - -echo "Making sure python3 and virtualenv are installed" +echo "== Making sure python3 and virtualenv are installed ==" python3 --version || apt-get install -y python3 python3 -m venv -h >/dev/null 2>&1 || apt-get install -y python3-venv -echo "Create virualenv" + +echo "== Create yubilock group ==" +addgroup --system yubilock +echo "== Add $username to yubilock group ==" +usermod -a -G yubilock "$username" + + + +echo "== Create virualenv ==" [ -f "$install_dir/venv/bin/activate" ] || python3 -m venv "$install_dir/venv" . "$install_dir/venv/bin/activate" pip install setuptools wheel pip install -r "$script_dir/requirements.txt" -echo "Copy over application files" -cp "$script_dir/xscreensaver_yubilock.py" "$install_dir" -cp "$script_dir/uninstall.sh" "$install_dir" -cp "$script_dir/config_example.ini" "$install_dir/config.ini" -# Remove first line from config -sed -i '1d' "$install_dir/config.ini" +echo "== Copy over application files ==" +cp "$script_dir/bin/xscreensaver_yubilock.py" "$install_dir" +cp "$script_dir/bin/uninstall.sh" "$install_dir" +cp "$script_dir/bin/kill_screensaver_graphic_program.sh" "$install_dir" +cp "$script_dir/bin/config_system.ini" "$install_dir/config.ini" + +chown -R root:yubilock "$install_dir" +chmod 771 "$install_dir" + + # Add yubikey serials to config -[ -n "$serials" ] && sed -i "s+^yubikey_serial\ =.*+yubikey_serial\ =\ $serials+g" "$install_dir/config.ini" +if [ -n "$serials" ]; then + homedir=`eval echo ~"$username"` + echo "Homedir: $homedir" + [ -f "$homedir/.yubilock" ] || ( cp "$script_dir/bin/config_user.ini" "$homedir/.yubilock" && chown "$username:$username" "$homedir/.yubilock") + sed -i "s+^yubikey_serial.*+yubikey_serial\ =\ $serials+g" "$homedir/.yubilock" + echo "Add yubikey serial(s) to $homedir/.yubilock" +fi -chown -R yubilock:yubilock "$install_dir" -chown root:yubilock "$install_dir" -chmod 775 "$install_dir" -echo "Create logging directory" +echo "== Create logging directory ==" mkdir -p "$logging_dir" chown --from=root:root root:yubilock "$logging_dir" chmod 775 "$logging_dir" sed -i "s+^logfile\ =.*+logfile\ =\ ${logging_dir}daemon.log+g" "$install_dir/config.ini" -echo "Allow yubilock user access to X host" -touch "$install_dir/.Xauthority" -chown yubilock:yubilock "$install_dir/.Xauthority" -hexkey=`sudo -u $username xauth list | cut -d ' ' -f 5` -export XAUTHORITY="/opt/yubilock/.Xauthority" -echo sudo -u yubilock xauth add \":0\" . "$hexkey" -sudo -u yubilock xauth add ":0" . "$hexkey" - -echo "Fix udev usb rights for yubilock" +echo "== Fix udev usb rights for yubilock group ==" cp "$script_dir/debian/91-usbftdi.rules" '/etc/udev/rules.d/' chown root:root '/etc/udev/rules.d/91-usbftdi.rules' udevadm control --reload-rules if [ "$use_systemd" = 'yes' ]; then - echo "Enable as systemd service" - cp "$script_dir/debian/yubilock.service" "/etc/systemd/system" - sed -i "s+^ExecStart=.*+ExecStart=${install_dir}venv/bin/python ${install_dir}xscreensaver_yubilock.py+g" '/etc/systemd/system/yubilock.service' - systemctl enable yubilock.service + echo "== Enable as systemd service ==" + mkdir -p "/home/$username/.config/systemd/user" + cp "$script_dir/debian/yubilock.service" "/home/$username/.config/systemd/user" + sed -i "s+^ExecStart=.*+ExecStart=${install_dir}venv/bin/python ${install_dir}xscreensaver_yubilock.py -v+g" "/home/$username/.config/systemd/user/yubilock.service" + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user daemon-reload' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user enable yubilock.service' + # su is used for systemctl user units because systemctl matches executing uid to unit owner uid. See: + # https://unix.stackexchange.com/questions/483948/inspect-unit-status-for-user-units-with-systemctl-as-root/485063#485063 else - # Make sure service is not previously installed - systemctl stop yubilock.service >/dev/null 2>&1 - systemctl disable yubilock.service >/dev/null 2>&1 - rm '/etc/systemd/system/yubilock.service' >/dev/null 2>&1 - rm '/usr/lib/systemd/system/yubilock.service' >/dev/null 2>&1 - systemctl daemon-reload - systemctl reset-failed + # Make sure service is removed if previously installed + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user stop yubilock.service >/dev/null 2>&1' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user disable yubilock.service >/dev/null 2>&1' + rm "/home/$username/.config/systemd/user/yubilock.service" >/dev/null 2>&1 + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user daemon-reload' + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user reset-failed' fi -echo "xscreensaver-yubilock is installed!" +echo "== xscreensaver-yubilock is installed! ==" +echo "== to enable yubilock, please restart your device == +exit 0 +# Due to loginctl not updating user groups, the user has to restart before the service can be started. if [ "$use_systemd" = 'yes' ]; then echo "Do you wish to start the daemon now? WARNING: If the specified yubikey is not plugged in, your machine will lock. Alternatively, you can start the service using 'sudo systemctl start yubilock.service' or wait for next login." read -p "Start daemon? (y/N) " start_daemon @@ -140,7 +149,7 @@ if [ "$use_systemd" = 'yes' ]; then n|N|0 ) ;; * ) - systemctl start yubilock.service;; + su "$username" -c 'XDG_RUNTIME_DIR=/run/user/$UID systemctl --user start yubilock.service';; esac fi