#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright 2024 SUSE LLC

set -euo pipefail

d_styled()
{
	dialog --title $"SSH Key Enrollment" "$@"
}

handle_pubkeys()
{
	local filename="$1"
	if ! [ -s "$filename" ]; then
		d_styled --msgbox $"No public keys received!" 0 0
		return 1
	fi

	local text="$(printf $"Did the\n'ssh-pairing: Received %d public keys'\nmessage appear on the client (%s)?" \
	              "$(wc -l <"$filename")" "$(awk '{print $3; exit}' "$filename")")"
	if ! d_styled --defaultno --yesno "$text" 0 0; then
		d_styled --msgbox $"The message is important to ensure the client identity. Aborting." 0 0
		return 1
	fi

	local imported=0

	exec {keylistfd}<"$filename"
	while read -r -u "$keylistfd" keyline; do
		local text="$(echo -e $"Do you want to import the following key?\n"; echo "$keyline" | ssh-keygen -l -v -f -)"
		if d_styled --no-nl-expand --no-collapse --defaultno --yesno "$text" 0 0; then
			(umask 077; mkdir -p ~/.ssh)
			echo "$keyline" >> ~/.ssh/authorized_keys
			let imported+=1
		fi
	done
	# Word splitting makes it necessary to use eval here.
	eval "exec ${keylistfd}>&-"

	if [ "$imported" -eq 0 ]; then
		d_styled --msgbox $"No keys imported." 0 0
		return 2
	else
		text="$(printf $"Imported %d keys" "$imported")"
		d_styled --msgbox "$text" 0 0
	fi
}

tmpdir="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -r ${tmpdir@Q}" EXIT

ssh_enroll_do_config()
{
	# Stop sshd.service temporarily if needed
	local start_sshd_again=0
	if systemctl -q is-active sshd.service; then
		systemctl stop sshd.service
		start_sshd_again=1
	fi

	# Make sure host keys exist
	if command -v sshd-gen-keys-start >/dev/null; then
		# Called by sshd.service on openSUSE
		sshd-gen-keys-start >/dev/null
	else
		ssh-keygen -A
	fi

	# Start the server in the background and show a dialog with information while it's running
	# and offer the option to cancel the process.
	{
		echo $"Please connect to one of the addresses with ssh:"
		hostname -I
		echo
		echo $"Please verify that the host key matches:"
		for i in /etc/ssh/ssh_host_*key.pub; do ssh-keygen -l -f "$i"; done | awk '{ print $2" "$4 }'
		echo

		echo $"Randomart for ssh -o \"VisualHostKey yes\":"
		# Display the randomart for all key types next to each other
		local allartlines=()
		for i in /etc/ssh/ssh_host_*key.pub; do
			local artlines=()
			readarray -t artlines < <(ssh-keygen -l -v -f "$i" | tail -n+2)
			for (( j=0; j < "${#artlines[@]}"; j++ )); do
				allartlines[j]="${allartlines[j]-}${artlines[j]} "
			done
		done
		( IFS=$'\n'; echo -n "${allartlines[*]}" )
	} >"${tmpdir}/msg"

	ssh-pairing-server >"${tmpdir}/pubkeys" 2>"${tmpdir}/servererr" &
	local serverpid=$!

	# Have to use dialog directly here, a shell in between would eat signals
	dialog --title $"SSH Key Enrollment" --exit-label $"Cancel" --textbox "${tmpdir}/msg" 0 0 &
	local dialogpid=$!

	# Wait for either the server or dialog to exit
	local exitcode=0
	local who=0
	wait -n -p who "$serverpid" "$dialogpid" || exitcode=$?
	if [ "$who" -eq "$serverpid" ]; then
		kill "$dialogpid" # Server exited, kill the dialog
		wait "$dialogpid" || :
	else
		kill "$serverpid" # Dialog exited, kill the server
		wait "$serverpid" || :
	fi

	if [ "$start_sshd_again" != "0" ]; then
		systemctl start sshd.service || d_styled --msgbox $"Failed to start sshd.service again" 0 0
	fi

	if [ "$who" -eq "$serverpid" ] && [ "$exitcode" -eq 0 ]; then
		handle_pubkeys "${tmpdir}/pubkeys"
		return $?
	elif [ "$who" -eq "$serverpid" ]; then
		d_styled --msgbox $"ssh-pairing-server exited with ${exitcode}:\n""$(cat ${tmpdir}/servererr)" 0 0
		return 1
	else
		d_styled --msgbox $"Key enrollment cancelled by user request" 0 0
		return 1
	fi
}

ssh_enroll_do_config
