156 lines
5.8 KiB
Bash
Executable file
156 lines
5.8 KiB
Bash
Executable file
#!/bin/bash
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
LOG_DIR="/var/log/BastionGuard"
|
|
LOG_FILE="$LOG_DIR/phishing_updater.log"
|
|
PHISH_SYS="/usr/share/BastionGuard/data/phishing"
|
|
DNSMASQ_CONF="/etc/dnsmasq.d/BastionGuard-blacklist.conf"
|
|
DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
if [[ ${EUID} -ne 0 ]]; then
|
|
echo "BastionGuard-phishing-updater: must run as root via pkexec" >&2
|
|
exit 77
|
|
fi
|
|
|
|
install -d -o root -g root -m 0755 "$LOG_DIR" "$PHISH_SYS"
|
|
echo "[$DATE] Avvio aggiornamento blacklist..." >> "$LOG_FILE"
|
|
|
|
# The systemd timer may invoke the helper without staged input. Preserve the
|
|
# old no-op behaviour, but never consume predictable files from /tmp.
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "[$DATE] Nessun staging directory fornita; nessun aggiornamento da installare." >> "$LOG_FILE"
|
|
exit 0
|
|
fi
|
|
if [[ $# -ne 1 ]]; then
|
|
echo "Uso: $0 <staging-dir>" >&2
|
|
exit 64
|
|
fi
|
|
|
|
STAGE="$1"
|
|
CALLER_UID="${PKEXEC_UID:-${SUDO_UID:-}}"
|
|
if [[ -z "$CALLER_UID" || ! "$CALLER_UID" =~ ^[0-9]+$ || "$CALLER_UID" -eq 0 ]]; then
|
|
echo "BastionGuard-phishing-updater: caller UID non affidabile" >&2
|
|
exit 65
|
|
fi
|
|
|
|
if [[ -L "$STAGE" || ! -d "$STAGE" ]]; then
|
|
echo "BastionGuard-phishing-updater: staging directory non valida" >&2
|
|
exit 66
|
|
fi
|
|
STAGE_REAL=$(realpath -e -- "$STAGE")
|
|
case "$STAGE_REAL" in
|
|
/tmp/bastionguard-phish.*) ;;
|
|
*) echo "BastionGuard-phishing-updater: staging path non autorizzato" >&2; exit 67 ;;
|
|
esac
|
|
|
|
if [[ $(stat -c '%u' -- "$STAGE_REAL") != "$CALLER_UID" || \
|
|
$(stat -c '%a' -- "$STAGE_REAL") != "700" ]]; then
|
|
echo "BastionGuard-phishing-updater: owner/mode staging non validi" >&2
|
|
exit 68
|
|
fi
|
|
|
|
validate_stage_file() {
|
|
local f="$1"
|
|
[[ -f "$f" && ! -L "$f" ]] || return 1
|
|
[[ $(stat -c '%u' -- "$f") == "$CALLER_UID" ]] || return 1
|
|
[[ $(stat -c '%a' -- "$f") == "600" ]] || return 1
|
|
}
|
|
|
|
BLACKLIST="$STAGE_REAL/blacklist.txt"
|
|
BLACKLIST_REDUCE="$STAGE_REAL/blacklist-reduce.txt"
|
|
DNSMASQ_STAGE="$STAGE_REAL/dnsmasq-blacklist.conf"
|
|
|
|
validate_stage_file "$BLACKLIST" || { echo "blacklist staging non valida" >&2; exit 69; }
|
|
validate_stage_file "$BLACKLIST_REDUCE" || { echo "blacklist-reduce staging non valida" >&2; exit 69; }
|
|
validate_stage_file "$DNSMASQ_STAGE" || { echo "dnsmasq staging non valida" >&2; exit 69; }
|
|
|
|
# Snapshot the caller-owned inputs into a root-only directory before parsing.
|
|
# The final install is always performed from this immutable snapshot, so a
|
|
# caller cannot race validation by swapping the original /tmp files.
|
|
install -d -o root -g root -m 0700 /var/lib/BastionGuard
|
|
ROOT_STAGE=$(mktemp -d /var/lib/BastionGuard/phishing-stage.XXXXXX)
|
|
trap 'rm -rf -- "$ROOT_STAGE"' EXIT
|
|
install -m 0600 -- "$BLACKLIST" "$ROOT_STAGE/blacklist.txt"
|
|
install -m 0600 -- "$BLACKLIST_REDUCE" "$ROOT_STAGE/blacklist-reduce.txt"
|
|
install -m 0600 -- "$DNSMASQ_STAGE" "$ROOT_STAGE/dnsmasq-blacklist.conf"
|
|
BLACKLIST="$ROOT_STAGE/blacklist.txt"
|
|
BLACKLIST_REDUCE="$ROOT_STAGE/blacklist-reduce.txt"
|
|
DNSMASQ_STAGE="$ROOT_STAGE/dnsmasq-blacklist.conf"
|
|
|
|
validate_blacklist() {
|
|
local f="$1" line
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
[[ -z "$line" || "$line" == \#* ]] && continue
|
|
[[ ${#line} -le 4096 ]] || return 1
|
|
# Backend.cpp emits URLs, domains or IPs only. Reject control/whitespace
|
|
# and arbitrary file content before it can become world-readable.
|
|
if [[ "$line" =~ ^https?://[^[:space:][:cntrl:]]+$ ]]; then
|
|
continue
|
|
fi
|
|
if [[ "$line" =~ ^[A-Za-z0-9._:-]+$ && "$line" == *.* ]]; then
|
|
continue
|
|
fi
|
|
return 1
|
|
done < "$f"
|
|
}
|
|
|
|
validate_blacklist "$BLACKLIST" || { echo "blacklist contenuto non valido" >&2; exit 70; }
|
|
validate_blacklist "$BLACKLIST_REDUCE" || { echo "blacklist-reduce contenuto non valido" >&2; exit 70; }
|
|
|
|
# Only the directive generated by Backend.cpp is accepted. This blocks an
|
|
# attacker from smuggling dhcp-script= or any other executable dnsmasq option.
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
[[ -z "$line" ]] && continue
|
|
if [[ ! "$line" =~ ^address=/[A-Za-z0-9.-]+/127\.0\.0\.2$ ]]; then
|
|
echo "BastionGuard-phishing-updater: direttiva dnsmasq rifiutata" >&2
|
|
exit 71
|
|
fi
|
|
done < "$DNSMASQ_STAGE"
|
|
|
|
install -m 0644 -- "$BLACKLIST" "$PHISH_SYS/blacklist.txt"
|
|
install -m 0644 -- "$BLACKLIST_REDUCE" "$PHISH_SYS/blacklist-reduce.txt"
|
|
echo "[$DATE] blacklist installate" >> "$LOG_FILE"
|
|
|
|
# Copy the caller's whitelist only if it is a regular, caller-owned file.
|
|
REAL_HOME=$(getent passwd "$CALLER_UID" | cut -d: -f6)
|
|
if [[ -n "$REAL_HOME" ]]; then
|
|
WL_TXT="$REAL_HOME/.local/share/BastionGuard/phishing/whitelist.txt"
|
|
if [[ -f "$WL_TXT" && ! -L "$WL_TXT" && $(stat -c '%u' -- "$WL_TXT") == "$CALLER_UID" ]]; then
|
|
install -m 0600 -- "$WL_TXT" "$ROOT_STAGE/whitelist.txt"
|
|
WL_OK=1
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
[[ -z "$line" || "$line" == \#* ]] && continue
|
|
if [[ ! "$line" =~ ^[A-Za-z0-9.-]+$ ]]; then WL_OK=0; break; fi
|
|
done < "$ROOT_STAGE/whitelist.txt"
|
|
if [[ "$WL_OK" -eq 1 ]]; then
|
|
install -m 0644 -- "$ROOT_STAGE/whitelist.txt" "$PHISH_SYS/whitelist.txt"
|
|
echo "[$DATE] whitelist utente copiata in sys" >> "$LOG_FILE"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
install -d -o root -g root -m 0755 /etc/dnsmasq.d
|
|
install -m 0644 -- "$DNSMASQ_STAGE" "$DNSMASQ_CONF"
|
|
|
|
DNSMASQ_OK=0
|
|
if dnsmasq --test > "$LOG_DIR/dnsmasq-test.log" 2>&1; then
|
|
DNSMASQ_OK=1
|
|
else
|
|
echo "[$DATE] dnsmasq --test fallito, skip reload" >> "$LOG_FILE"
|
|
fi
|
|
|
|
SCANNER_ACTIVE=0
|
|
systemctl is-active --quiet BastionGuard-phishing-scanner.service && SCANNER_ACTIVE=1 || true
|
|
|
|
if [[ "$DNSMASQ_OK" -eq 1 ]]; then
|
|
systemctl restart dnsmasq
|
|
echo "[$DATE] dnsmasq ricaricato" >> "$LOG_FILE"
|
|
fi
|
|
if [[ "$SCANNER_ACTIVE" -eq 1 ]]; then
|
|
systemctl restart BastionGuard-phishing-scanner.service
|
|
echo "[$DATE] Scanner riavviato" >> "$LOG_FILE"
|
|
fi
|
|
|
|
echo "[$DATE] Aggiornamento completato" >> "$LOG_FILE"
|
|
exit 0
|