Files
fedora-tools/plasma-fingerprint-workaround/plasma-fingerprint-workaround
T

307 lines
8.1 KiB
Bash
Executable File

#!/usr/bin/bash
set -euo pipefail
PATH=/usr/sbin:/usr/bin
readonly data_dir=/usr/share/plasma-fingerprint-workaround
readonly payload_config="$data_dir/payload.conf"
readonly program=${0##*/}
temporary_dir=
usage() {
cat <<EOF
Usage:
$program status
$program enable [--rpm PATH] [--force] [--yes]
$program disable [--yes]
Without --rpm, enable downloads the tested prebuilt package. A local package
must have been built from this tool's KScreenLocker spec. --force permits a
base-version mismatch, including an intentional downgrade.
EOF
}
die() {
printf 'Error: %s\n' "$*" >&2
exit 1
}
require_root() {
(( EUID == 0 )) || die "run this command as root"
}
require_fedora() {
# shellcheck source=/dev/null
source /etc/os-release
[[ ${ID:-} == fedora ]] || die "this workaround only supports Fedora Linux"
}
cleanup() {
[[ -n $temporary_dir ]] || return
rm -f -- "$temporary_dir/kscreenlocker.rpm"
rmdir -- "$temporary_dir"
}
package_value() {
local mode=$1
local format=$2
local package=$3
rpm "$mode" --qf "$format" -- "$package"
}
package_base() {
local mode=$1
local package=$2
local provides
provides=$(rpm "$mode" --provides -- "$package")
sed -n 's/^plasma-fingerprint-workaround-base = //p' <<<"$provides"
}
is_workaround_package() {
local mode=$1
local package=$2
local provides
provides=$(rpm "$mode" --provides -- "$package")
grep -qx 'plasma-fingerprint-workaround-payload = 1' <<<"$provides"
}
is_legacy_workaround() {
[[ $(installed_value '%{RELEASE}') =~ \.ajp[[:digit:]]+$ ]]
}
is_any_workaround() {
is_workaround_package -q kscreenlocker || is_legacy_workaround
}
installed_value() {
package_value -q "$1" kscreenlocker
}
installed_base() {
local base
base=$(package_base -q kscreenlocker)
if [[ -n $base ]]; then
printf '%s\n' "$base"
elif is_legacy_workaround; then
printf '%s-%s\n' \
"$(installed_value '%{VERSION}')" \
"$(installed_value '%{RELEASE}' | sed 's/\.ajp[[:digit:]]*$//')"
else
installed_value '%{EVR}'
fi
}
show_status() {
if ! rpm -q kscreenlocker >/dev/null 2>&1; then
printf 'KScreenLocker is not installed.\n'
return 1
fi
if is_workaround_package -q kscreenlocker; then
printf 'Fingerprint workaround: enabled\n'
printf 'Installed package: kscreenlocker-%s.%s\n' \
"$(installed_value '%{EVR}')" "$(installed_value '%{ARCH}')"
printf 'Based on Fedora package: kscreenlocker-%s\n' "$(installed_base)"
elif is_legacy_workaround; then
printf 'Fingerprint workaround: enabled (legacy unmanaged build)\n'
printf 'Installed package: kscreenlocker-%s.%s\n' \
"$(installed_value '%{EVR}')" "$(installed_value '%{ARCH}')"
printf 'Based on Fedora package: kscreenlocker-%s\n' "$(installed_base)"
else
printf 'Fingerprint workaround: disabled\n'
printf 'Installed package: kscreenlocker-%s.%s\n' \
"$(installed_value '%{EVR}')" "$(installed_value '%{ARCH}')"
fi
}
confirm() {
local answer
if (( assume_yes )); then
return
fi
printf 'Continue? [y/N] '
read -r answer || exit 0
[[ $answer == y || $answer == Y ]] || exit 0
}
load_payload_config() {
[[ -r $payload_config ]] || die "payload configuration is missing"
unset payload_url payload_sha256
# This root-owned file is installed by the controller RPM.
# shellcheck source=/dev/null
source "$payload_config"
[[ ${payload_url:-} == https://* ]] || die "invalid payload URL"
[[ ${payload_sha256:-} =~ ^[[:xdigit:]]{64}$ ]] ||
die "invalid payload checksum"
}
validate_candidate() {
local candidate=$1
local name arch candidate_evr candidate_base current_base current_evr
rpm -K --nosignature "$candidate" >/dev/null || die "the RPM is corrupt"
name=$(package_value -qp '%{NAME}' "$candidate")
[[ $name == kscreenlocker ]] ||
die "expected a kscreenlocker RPM, got $name"
is_workaround_package -qp "$candidate" ||
die "the RPM was not built from this workaround's KScreenLocker spec"
arch=$(package_value -qp '%{ARCH}' "$candidate")
[[ $arch == "$(installed_value '%{ARCH}')" ]] ||
die "RPM architecture $arch does not match installed architecture $(installed_value '%{ARCH}')"
candidate_base=$(package_base -qp "$candidate")
[[ -n $candidate_base ]] || die "the RPM has no base-version marker"
candidate_evr=$(package_value -qp '%{EVR}' "$candidate")
current_base=$(installed_base)
current_evr=$(installed_value '%{EVR}')
printf 'Installed: kscreenlocker-%s\n' "$current_evr"
printf 'Candidate: kscreenlocker-%s (based on %s)\n' \
"$candidate_evr" "$candidate_base"
if [[ $candidate_base != "$current_base" ]]; then
if (( ! force )); then
die "candidate base $candidate_base does not match installed base $current_base; use --force to allow the version change"
fi
printf 'Warning: forcing a KScreenLocker base-version change.\n' >&2
fi
}
enable_workaround() {
local rpm_path='' candidate
local -a dnf_args
force=0
assume_yes=0
while (( $# )); do
case $1 in
--rpm)
(( $# >= 2 )) || die "--rpm requires a path"
rpm_path=$2
shift 2
;;
--force)
force=1
shift
;;
--yes)
assume_yes=1
shift
;;
*)
die "unknown enable option: $1"
;;
esac
done
require_root
require_fedora
rpm -q kscreenlocker >/dev/null 2>&1 || die "KScreenLocker is not installed"
umask 077
temporary_dir=$(mktemp -d /var/tmp/plasma-fingerprint-workaround.XXXXXX)
trap cleanup EXIT
candidate="$temporary_dir/kscreenlocker.rpm"
if [[ -n $rpm_path ]]; then
[[ -f $rpm_path ]] || die "RPM not found: $rpm_path"
install -m 0600 -- "$rpm_path" "$candidate"
else
load_payload_config
printf 'Downloading the tested KScreenLocker build...\n'
curl --fail --location --show-error --silent \
--proto '=https' --proto-redir '=https' \
--output "$candidate" "$payload_url"
printf '%s %s\n' "$payload_sha256" "$candidate" |
sha256sum --check --status - || die "downloaded RPM checksum does not match"
fi
validate_candidate "$candidate"
printf '\nThis replaces Fedora\x27s security-sensitive KScreenLocker package.\n'
confirm
dnf_args=(install --assumeyes)
(( force )) && dnf_args+=(--allow-downgrade)
dnf5 "${dnf_args[@]}" "$candidate"
is_workaround_package -q kscreenlocker ||
die "DNF completed without enabling the workaround"
printf 'Fingerprint workaround enabled. Reboot before testing it.\n'
}
disable_workaround() {
assume_yes=0
while (( $# )); do
case $1 in
--yes)
assume_yes=1
shift
;;
*)
die "unknown disable option: $1"
;;
esac
done
require_root
require_fedora
if ! is_any_workaround; then
printf 'Fingerprint workaround is already disabled.\n'
return
fi
printf 'This will replace the workaround with Fedora\x27s current KScreenLocker package.\n'
confirm
dnf5 \
--repo=fedora,updates \
--refresh \
distro-sync \
--assumeyes \
kscreenlocker
if is_any_workaround; then
die "no suitable Fedora KScreenLocker package was installed"
fi
printf 'Fingerprint workaround disabled. Reboot before testing the lock screen.\n'
}
command=${1:-}
[[ -n $command ]] || {
usage
exit 2
}
shift
case $command in
status)
(( $# == 0 )) || die "status takes no options"
show_status
;;
enable)
enable_workaround "$@"
;;
disable)
disable_workaround "$@"
;;
-h|--help|help)
usage
;;
*)
usage >&2
exit 2
;;
esac