1643 lines
61 KiB
Bash
1643 lines
61 KiB
Bash
#!/bin/bash
|
|
# /usr/share/BastionGuard/data/scripts/install-ca-system.sh
|
|
|
|
set -euo pipefail
|
|
|
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
|
|
CA_NAME="BastionGuard-CA"
|
|
|
|
log() { echo "[BastionGuard] $*"; }
|
|
warn() { echo "[BastionGuard] ⚠️ $*" >&2; }
|
|
err() { echo "[BastionGuard] ❌ $*" >&2; }
|
|
|
|
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
# ── Utente/home reali + layout CA utente ─────────────────────────────────────
|
|
# Regola: anche se questo helper gira come root (pkexec/sudo), i file privati
|
|
# della CA devono vivere nella HOME dell'utente grafico/reale e devono restare
|
|
# posseduti da quell'utente. Root serve solo per copiare la CA pubblica nei
|
|
# trust store di sistema.
|
|
|
|
get_real_uid() {
|
|
# pkexec espone l'UID del chiamante; sudo espone SUDO_UID. Sono più
|
|
# affidabili di USER/HOME, perché in root diventano spesso root:/root.
|
|
if [[ -n "${PKEXEC_UID:-}" && "${PKEXEC_UID:-}" =~ ^[0-9]+$ && "${PKEXEC_UID}" != "0" ]]; then
|
|
printf '%s' "$PKEXEC_UID"
|
|
return 0
|
|
fi
|
|
if [[ -n "${SUDO_UID:-}" && "${SUDO_UID:-}" =~ ^[0-9]+$ && "${SUDO_UID}" != "0" ]]; then
|
|
printf '%s' "$SUDO_UID"
|
|
return 0
|
|
fi
|
|
|
|
local u=""
|
|
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER:-}" != "root" ]] && id "$SUDO_USER" >/dev/null 2>&1; then
|
|
id -u "$SUDO_USER"
|
|
return 0
|
|
fi
|
|
|
|
u="$(logname 2>/dev/null || true)"
|
|
if [[ -n "$u" && "$u" != "root" ]] && id "$u" >/dev/null 2>&1; then
|
|
id -u "$u"
|
|
return 0
|
|
fi
|
|
|
|
for cand in "${PKEXEC_USER:-}" "${USER:-}" "${LOGNAME:-}"; do
|
|
if [[ -n "${cand:-}" && "$cand" != "root" ]] && id "$cand" >/dev/null 2>&1; then
|
|
id -u "$cand"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
get_user_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f1
|
|
}
|
|
|
|
get_group_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f4 | xargs -r getent group | cut -d: -f1
|
|
}
|
|
|
|
get_home_for_uid() {
|
|
local uid="$1"
|
|
[[ -n "$uid" ]] || return 1
|
|
getent passwd "$uid" | cut -d: -f6
|
|
}
|
|
|
|
path_under() {
|
|
local child="$1"
|
|
local parent="$2"
|
|
[[ -n "$child" && -n "$parent" ]] || return 1
|
|
child="$(readlink -m -- "$child")"
|
|
parent="$(readlink -m -- "$parent")"
|
|
[[ "$child" == "$parent" || "$child" == "$parent"/* ]]
|
|
}
|
|
|
|
selinux_status() {
|
|
if have_cmd getenforce; then
|
|
getenforce 2>/dev/null || printf 'Disabled'
|
|
elif [[ -r /sys/fs/selinux/enforce ]]; then
|
|
if [[ "$(cat /sys/fs/selinux/enforce 2>/dev/null || echo 0)" == "1" ]]; then
|
|
printf 'Enforcing'
|
|
else
|
|
printf 'Permissive'
|
|
fi
|
|
else
|
|
printf 'Disabled'
|
|
fi
|
|
}
|
|
|
|
selinux_enabled() {
|
|
local s
|
|
s="$(selinux_status 2>/dev/null || printf 'Disabled')"
|
|
[[ "$s" != "Disabled" ]]
|
|
}
|
|
|
|
selinux_enforcing() {
|
|
[[ "$(selinux_status 2>/dev/null || printf 'Disabled')" == "Enforcing" ]]
|
|
}
|
|
|
|
restore_selinux_context() {
|
|
# Fedora/RHEL hanno SELinux attivo di default: dopo install/chown/mkdir
|
|
# ripristiniamo sempre i label standard. Su distro senza SELinux non è fatale.
|
|
if ! have_cmd restorecon; then
|
|
if selinux_enabled; then
|
|
warn "SELinux è attivo ma restorecon non è disponibile; installare policycoreutils"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
local p
|
|
for p in "$@"; do
|
|
[[ -n "${p:-}" && -e "$p" ]] || continue
|
|
if ! restorecon -RF "$p" >/dev/null 2>&1; then
|
|
selinux_enforcing && warn "restorecon fallito su: $p"
|
|
fi
|
|
done
|
|
}
|
|
|
|
fix_selinux_system_trust_contexts() {
|
|
selinux_enabled || return 0
|
|
restore_selinux_context \
|
|
/etc/pki/ca-trust \
|
|
/etc/pki/ca-trust/source \
|
|
/etc/pki/ca-trust/source/anchors \
|
|
/etc/pki/tls \
|
|
/etc/pki/nssdb \
|
|
/usr/share/pki/nssdb \
|
|
/etc/ssl/certs \
|
|
/usr/share/ca-certificates \
|
|
/usr/local/share/ca-certificates \
|
|
/etc/ca-certificates 2>/dev/null || true
|
|
}
|
|
|
|
fix_selinux_user_browser_contexts() {
|
|
selinux_enabled || return 0
|
|
[[ -n "${REAL_HOME:-}" ]] || return 0
|
|
restore_selinux_context \
|
|
"$REAL_HOME/.pki" \
|
|
"$REAL_HOME/.mozilla" \
|
|
"$REAL_HOME/.config/google-chrome" \
|
|
"$REAL_HOME/.config/chromium" \
|
|
"$REAL_HOME/.config/BraveSoftware" \
|
|
"$REAL_HOME/.config/vivaldi" \
|
|
"$REAL_HOME/.config/opera" \
|
|
"$REAL_HOME/.config/microsoft-edge" \
|
|
"$REAL_HOME/.config/thorium" \
|
|
"$REAL_HOME/.config/ungoogled-chromium" \
|
|
"$REAL_HOME/.var/app" \
|
|
"$REAL_HOME/snap" 2>/dev/null || true
|
|
}
|
|
|
|
check_fedora_selinux_requirements() {
|
|
if distro_is "fedora" || distro_is "rhel" || distro_is "centos"; then
|
|
selinux_enabled && log "SELinux rilevato: $(selinux_status)" || true
|
|
have_cmd update-ca-trust || warn "Fedora/RHEL: update-ca-trust non trovato"
|
|
have_cmd certutil || warn "Fedora/RHEL: certutil non trovato; installare nss-tools per import NSS/browser"
|
|
if selinux_enabled && ! have_cmd restorecon; then
|
|
warn "Fedora/RHEL: restorecon non trovato; installare policycoreutils"
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
run_as_real_user() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]] || return 1
|
|
|
|
local runtime_dir=""
|
|
local bus_addr=""
|
|
if [[ -n "${REAL_UID:-}" && -d "/run/user/${REAL_UID}" ]]; then
|
|
runtime_dir="/run/user/${REAL_UID}"
|
|
[[ -S "${runtime_dir}/bus" ]] && bus_addr="unix:path=${runtime_dir}/bus"
|
|
fi
|
|
|
|
if have_cmd runuser; then
|
|
local env_args=(
|
|
"HOME=$REAL_HOME"
|
|
"USER=$REAL_USER"
|
|
"LOGNAME=$REAL_USER"
|
|
"XDG_DATA_HOME=$REAL_HOME/.local/share"
|
|
"XDG_CONFIG_HOME=$REAL_HOME/.config"
|
|
)
|
|
[[ -n "$runtime_dir" ]] && env_args+=("XDG_RUNTIME_DIR=$runtime_dir")
|
|
[[ -n "$bus_addr" ]] && env_args+=("DBUS_SESSION_BUS_ADDRESS=$bus_addr")
|
|
|
|
runuser -u "$REAL_USER" -- env "${env_args[@]}" "$@"
|
|
elif have_cmd su; then
|
|
local env_cmd
|
|
env_cmd="HOME=$(printf '%q' "$REAL_HOME") USER=$(printf '%q' "$REAL_USER") LOGNAME=$(printf '%q' "$REAL_USER") XDG_DATA_HOME=$(printf '%q' "$REAL_HOME/.local/share") XDG_CONFIG_HOME=$(printf '%q' "$REAL_HOME/.config")"
|
|
[[ -n "$runtime_dir" ]] && env_cmd="$env_cmd XDG_RUNTIME_DIR=$(printf '%q' "$runtime_dir")"
|
|
[[ -n "$bus_addr" ]] && env_cmd="$env_cmd DBUS_SESSION_BUS_ADDRESS=$(printf '%q' "$bus_addr")"
|
|
su -s /bin/sh - "$REAL_USER" -c "$env_cmd $(printf '%q ' "$@")"
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
REAL_UID="$(get_real_uid || true)"
|
|
REAL_USER=""
|
|
REAL_GROUP=""
|
|
REAL_HOME=""
|
|
|
|
if [[ -n "$REAL_UID" ]]; then
|
|
REAL_USER="$(get_user_for_uid "$REAL_UID" || true)"
|
|
REAL_GROUP="$(get_group_for_uid "$REAL_UID" || true)"
|
|
REAL_HOME="$(get_home_for_uid "$REAL_UID" || true)"
|
|
fi
|
|
|
|
if [[ -z "$REAL_GROUP" && -n "$REAL_USER" ]]; then
|
|
REAL_GROUP="$REAL_USER"
|
|
fi
|
|
|
|
if [[ -n "$REAL_USER" && -n "$REAL_HOME" ]]; then
|
|
log "Utente reale: $REAL_USER ($REAL_HOME)"
|
|
else
|
|
warn "Impossibile determinare utente/home reali; userò solo path di sistema"
|
|
fi
|
|
|
|
USER_BG_DIR=""
|
|
USER_CA_DIR=""
|
|
if [[ -n "$REAL_HOME" ]]; then
|
|
USER_BG_DIR="$REAL_HOME/.local/share/BastionGuard"
|
|
USER_CA_DIR="$USER_BG_DIR/certs"
|
|
fi
|
|
|
|
fix_user_ca_permissions() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" && -n "${USER_BG_DIR:-}" && -n "${USER_CA_DIR:-}" ]] || return 0
|
|
[[ -d "$USER_BG_DIR" ]] || return 0
|
|
|
|
# Ripara anche directory intermedie create in precedenza da root.
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$USER_BG_DIR" 2>/dev/null || true
|
|
chmod 700 "$USER_BG_DIR" 2>/dev/null || true
|
|
[[ -d "$USER_CA_DIR" ]] && chmod 700 "$USER_CA_DIR" 2>/dev/null || true
|
|
|
|
if [[ -d "$USER_CA_DIR" ]]; then
|
|
find "$USER_CA_DIR" -type d -exec chmod 700 {} + 2>/dev/null || true
|
|
find "$USER_CA_DIR" -type f \( -name '*.key' -o -name '*.key.pem' \) -exec chmod 600 {} + 2>/dev/null || true
|
|
find "$USER_CA_DIR" -type f \( -name '*.crt' -o -name '*.crt.pem' -o -name '*.pem' \) ! -name '*.key.pem' -exec chmod 644 {} + 2>/dev/null || true
|
|
fi
|
|
|
|
restore_selinux_context "$USER_BG_DIR"
|
|
}
|
|
|
|
ensure_user_ca_dir() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" && -n "${USER_CA_DIR:-}" ]] || return 0
|
|
|
|
# Crea prima da root, poi corregge ownership/mode: funziona anche se
|
|
# ~/.local/share/BastionGuard era già stato creato male da root.
|
|
mkdir -p "$USER_CA_DIR"
|
|
fix_user_ca_permissions
|
|
}
|
|
|
|
# Il path della CA può essere passato come argomento, ma non deve mai cadere
|
|
# accidentalmente sotto /root quando conosciamo l'utente reale.
|
|
if [[ -n "${USER_CA_DIR:-}" ]]; then
|
|
ensure_user_ca_dir
|
|
fi
|
|
|
|
if [[ $# -ge 1 && -n "${1:-}" ]]; then
|
|
CA_SRC="$(readlink -m -- "$1")"
|
|
|
|
# Caso tipico del bug: il chiamante, essendo root, ha espanso HOME=/root.
|
|
# In quel caso correggiamo verso la home reale invece di generare certs root-owned.
|
|
if [[ -n "${REAL_HOME:-}" ]] && path_under "$CA_SRC" "/root"; then
|
|
warn "Path CA sotto /root rilevato ($CA_SRC); uso la home reale dell'utente"
|
|
CA_SRC="$USER_CA_DIR/intercept-ca.crt.pem"
|
|
fi
|
|
else
|
|
if [[ -n "${USER_CA_DIR:-}" ]]; then
|
|
CA_SRC="$USER_CA_DIR/intercept-ca.crt.pem"
|
|
else
|
|
CA_SRC="/etc/BastionGuard/certs/intercept-ca.crt.pem"
|
|
fi
|
|
fi
|
|
|
|
CA_DIR="$(dirname "$CA_SRC")"
|
|
if [[ -n "${REAL_HOME:-}" ]] && path_under "$CA_DIR" "$REAL_HOME"; then
|
|
mkdir -p "$CA_DIR"
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown -R "$REAL_USER:$REAL_GROUP" "$CA_DIR" 2>/dev/null || true
|
|
chmod 700 "$CA_DIR" 2>/dev/null || true
|
|
restore_selinux_context "$CA_DIR"
|
|
# Se è il percorso canonico di BastionGuard, ripara anche l'albero completo.
|
|
ensure_user_ca_dir
|
|
elif [[ "$CA_DIR" == /etc/* ]]; then
|
|
mkdir -p "$CA_DIR"
|
|
else
|
|
mkdir -p "$CA_DIR"
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown "$REAL_USER:$REAL_GROUP" "$CA_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
[ -f "$CA_SRC" ] || warn "CA non trovata: $CA_SRC — verrà generata automaticamente"
|
|
# ── Legge /etc/os-release in modo sicuro ──────────────────────────────────────
|
|
ID=""
|
|
ID_LIKE=""
|
|
[ -f /etc/os-release ] && . /etc/os-release
|
|
|
|
distro_is() {
|
|
[[ "${ID:-}" == "$1" ]] && return 0
|
|
[[ "${ID_LIKE:-}" == *"$1"* ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
check_fedora_selinux_requirements
|
|
|
|
# ── Valida il certificato sorgente ────────────────────────────────────────────
|
|
validate_ca() {
|
|
if ! openssl x509 -in "$CA_SRC" -noout >/dev/null 2>&1; then
|
|
err "Certificato non valido o non leggibile: $CA_SRC"
|
|
exit 1
|
|
fi
|
|
if ! openssl x509 -in "$CA_SRC" -noout -text 2>/dev/null | grep -q "CA:TRUE"; then
|
|
err "Il certificato non ha Basic Constraints CA:TRUE"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
|
|
# ── Pulizia vecchie CA BastionGuard ──────────────────────────────────────────
|
|
# Obiettivo: prima di installare/importare la CA corrente, rimuovere copie
|
|
# precedenti dai trust store di sistema e dai database NSS. Non tocca mai
|
|
# CA_SRC né la chiave privata: elimina solo vecchie copie pubbliche installate.
|
|
ca_fingerprint_sha256() {
|
|
local cert="$1"
|
|
[[ -f "$cert" ]] || return 1
|
|
openssl x509 -in "$cert" -noout -fingerprint -sha256 2>/dev/null \
|
|
| sed 's/^sha256 Fingerprint=//; s/^SHA256 Fingerprint=//'
|
|
}
|
|
|
|
current_ca_fingerprint_sha256() {
|
|
ca_fingerprint_sha256 "$CA_SRC"
|
|
}
|
|
|
|
is_current_ca_file() {
|
|
local cert="$1"
|
|
[[ -f "$cert" && -f "$CA_SRC" ]] || return 1
|
|
|
|
local fp_current fp_candidate
|
|
fp_current="$(current_ca_fingerprint_sha256 || true)"
|
|
fp_candidate="$(ca_fingerprint_sha256 "$cert" || true)"
|
|
|
|
[[ -n "$fp_current" && -n "$fp_candidate" && "$fp_current" == "$fp_candidate" ]]
|
|
}
|
|
|
|
remove_old_ca_file_if_needed() {
|
|
local cert="$1"
|
|
[[ -n "$cert" && -f "$cert" ]] || return 0
|
|
|
|
# Mai rimuovere il sorgente della CA corrente, anche se è in /etc.
|
|
if [[ "$(readlink -m -- "$cert")" == "$(readlink -m -- "$CA_SRC")" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Rimuove solo file che sembrano davvero CA/cert BastionGuard.
|
|
if ! openssl x509 -in "$cert" -noout >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
if ! openssl x509 -in "$cert" -noout -subject -issuer 2>/dev/null | grep -qiE 'BastionGuard|intercept'; then
|
|
return 0
|
|
fi
|
|
|
|
if is_current_ca_file "$cert"; then
|
|
log "CA già aggiornata nel trust store: $cert"
|
|
return 0
|
|
fi
|
|
|
|
log "Rimuovo vecchia CA BastionGuard: $cert"
|
|
rm -f -- "$cert" 2>/dev/null || warn "Impossibile rimuovere vecchia CA: $cert"
|
|
}
|
|
|
|
purge_old_system_ca_copies() {
|
|
[[ -f "$CA_SRC" ]] || return 0
|
|
log "Pulizia vecchie CA BastionGuard dai trust store di sistema…"
|
|
|
|
local candidates=()
|
|
candidates+=(
|
|
"/usr/local/share/ca-certificates/BastionGuard-ca.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard-ca.pem"
|
|
"/usr/local/share/ca-certificates/BastionGuard-CA.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard-CA.pem"
|
|
"/usr/local/share/ca-certificates/BastionGuard Intercept CA.crt"
|
|
"/usr/local/share/ca-certificates/BastionGuard Intercept CA.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard-ca.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard-ca.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard-CA.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard-CA.pem"
|
|
"/usr/share/ca-certificates/local/BastionGuard Intercept CA.crt"
|
|
"/usr/share/ca-certificates/local/BastionGuard Intercept CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-ca.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-ca.crt"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard-CA.crt"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard Intercept CA.pem"
|
|
"/etc/pki/ca-trust/source/anchors/BastionGuard Intercept CA.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-ca.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-ca.pem"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-CA.crt"
|
|
"/etc/ca-certificates/trust-source/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-ca.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-ca.crt"
|
|
"/etc/pki/trust/anchors/BastionGuard-CA.pem"
|
|
"/etc/pki/trust/anchors/BastionGuard-CA.crt"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-ca.pem"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-ca.crt"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-CA.pem"
|
|
"/usr/share/ca-certificates/bastionguard/BastionGuard-CA.crt"
|
|
)
|
|
|
|
local f
|
|
for f in "${candidates[@]}"; do
|
|
remove_old_ca_file_if_needed "$f"
|
|
done
|
|
|
|
# Cerca anche copie residue con nomi BastionGuard/intercept nelle directory
|
|
# dei trust store, senza scandire l'intero filesystem.
|
|
local roots=(
|
|
/usr/local/share/ca-certificates
|
|
/usr/share/ca-certificates/local
|
|
/etc/pki/ca-trust/source/anchors
|
|
/etc/ca-certificates/trust-source/anchors
|
|
/etc/pki/trust/anchors
|
|
/usr/share/ca-certificates/bastionguard
|
|
)
|
|
local root
|
|
for root in "${roots[@]}"; do
|
|
[[ -d "$root" ]] || continue
|
|
while IFS= read -r -d '' f; do
|
|
remove_old_ca_file_if_needed "$f"
|
|
done < <(find "$root" -maxdepth 1 -type f \
|
|
\( -iname '*bastionguard*.crt' -o -iname '*bastionguard*.pem' -o -iname '*intercept*.crt' -o -iname '*intercept*.pem' \) \
|
|
-print0 2>/dev/null || true)
|
|
done
|
|
|
|
# Pulisce le entry duplicate/obsolete su Debian-like; l'entry corretta
|
|
# verrà riscritta da install_debian_style().
|
|
[[ -f /etc/ca-certificates.conf ]] && sed -i '/[Bb]astion[Gg]uard\|[Ii]ntercept/d' /etc/ca-certificates.conf || true
|
|
|
|
fix_selinux_system_trust_contexts
|
|
}
|
|
|
|
nss_delete_matching_nicknames_root() {
|
|
local prefix="$1"
|
|
[[ -n "$prefix" ]] || return 0
|
|
|
|
local nick line
|
|
|
|
# Nickname noti.
|
|
for nick in "$CA_NAME" "BastionGuard Intercept CA" "BastionGuard CA" "BastionGuard-ca"; do
|
|
certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
# Nickname imprevisti ma riconoscibili in certutil -L.
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ [Bb]astion[Gg]uard|[Ii]ntercept ]] || continue
|
|
[[ "$line" =~ ^Certificate ]] && continue
|
|
[[ "$line" =~ ^-+ ]] && continue
|
|
nick="$(printf '%s\n' "$line" | sed -E 's/[[:space:]]+[A-Za-z,]+$//; s/[[:space:]]+$//')"
|
|
[[ -n "$nick" ]] || continue
|
|
certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done < <(certutil -L -d "$prefix" 2>/dev/null || true)
|
|
}
|
|
|
|
nss_delete_matching_nicknames_user() {
|
|
local prefix="$1"
|
|
[[ -n "$prefix" ]] || return 0
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]] || return 0
|
|
|
|
local nick line
|
|
|
|
# Nickname noti.
|
|
for nick in "$CA_NAME" "BastionGuard Intercept CA" "BastionGuard CA" "BastionGuard-ca"; do
|
|
run_as_real_user certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
# Nickname imprevisti ma riconoscibili in certutil -L.
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ [Bb]astion[Gg]uard|[Ii]ntercept ]] || continue
|
|
[[ "$line" =~ ^Certificate ]] && continue
|
|
[[ "$line" =~ ^-+ ]] && continue
|
|
nick="$(printf '%s\n' "$line" | sed -E 's/[[:space:]]+[A-Za-z,]+$//; s/[[:space:]]+$//')"
|
|
[[ -n "$nick" ]] || continue
|
|
run_as_real_user certutil -D -d "$prefix" -n "$nick" >/dev/null 2>&1 || true
|
|
done < <(run_as_real_user certutil -L -d "$prefix" 2>/dev/null || true)
|
|
}
|
|
|
|
close_running_browsers() {
|
|
# I browser tengono spesso aperti/lockati i DB NSS dei profili. Se restano
|
|
# in esecuzione, la rimozione delle vecchie CA o l'import della nuova CA può
|
|
# fallire o risultare visibile solo al riavvio. Per questo li chiudiamo in
|
|
# modo controllato prima dell'import.
|
|
[[ -n "${REAL_UID:-}" ]] || return 0
|
|
|
|
local patterns=(
|
|
'(^|/)(firefox|firefox-bin)([[:space:]]|$)'
|
|
'(^|/)(librewolf|librewolf-bin)([[:space:]]|$)'
|
|
'(^|/)(waterfox|waterfox-bin)([[:space:]]|$)'
|
|
'(^|/)(floorp|floorp-bin)([[:space:]]|$)'
|
|
'(^|/)(zen|zen-bin|zen-browser)([[:space:]]|$)'
|
|
'(^|/)(mullvadbrowser|mullvad-browser)([[:space:]]|$)'
|
|
'(^|/)(chromium|chromium-browser)([[:space:]]|$)'
|
|
'(^|/)(google-chrome|google-chrome-stable|google-chrome-beta|google-chrome-unstable|chrome)([[:space:]]|$)'
|
|
'(^|/)(brave|brave-browser|brave-browser-beta|brave-browser-nightly)([[:space:]]|$)'
|
|
'(^|/)(vivaldi|vivaldi-bin|vivaldi-snapshot)([[:space:]]|$)'
|
|
'(^|/)(opera|opera-browser|opera-beta|opera-developer)([[:space:]]|$)'
|
|
'(^|/)(microsoft-edge|microsoft-edge-beta|microsoft-edge-dev|msedge)([[:space:]]|$)'
|
|
'(^|/)(thorium|thorium-browser|ungoogled-chromium|epiphany|yandex-browser)([[:space:]]|$)'
|
|
'(org\.mozilla\.firefox|org\.chromium\.Chromium|com\.google\.Chrome|com\.google\.ChromeDev|com\.brave\.Browser|com\.vivaldi\.Vivaldi|com\.opera\.Opera|com\.microsoft\.Edge|io\.gitlab\.librewolf-community|net\.waterfox\.waterfox|one\.ablaze\.floorp|io\.github\.ungoogled_software\.ungoogled_chromium|com\.github\.Eloston\.UngoogledChromium|org\.gnome\.Epiphany)'
|
|
)
|
|
|
|
local pattern found=0
|
|
for pattern in "${patterns[@]}"; do
|
|
if pgrep -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1; then
|
|
found=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
[[ "$found" -eq 1 ]] || return 0
|
|
|
|
warn "Browser in esecuzione rilevati: li chiudo automaticamente prima dell'import certificati"
|
|
|
|
for pattern in "${patterns[@]}"; do
|
|
pkill -TERM -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
local i still_running
|
|
for i in $(seq 1 20); do
|
|
still_running=0
|
|
for pattern in "${patterns[@]}"; do
|
|
if pgrep -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1; then
|
|
still_running=1
|
|
break
|
|
fi
|
|
done
|
|
[[ "$still_running" -eq 0 ]] && { log "✅ Browser chiusi"; return 0; }
|
|
sleep 0.5
|
|
done
|
|
|
|
warn "Alcuni processi browser sono ancora attivi: forzo la chiusura"
|
|
for pattern in "${patterns[@]}"; do
|
|
pkill -KILL -u "$REAL_UID" -f -- "$pattern" >/dev/null 2>&1 || true
|
|
done
|
|
sleep 1
|
|
}
|
|
# ── Controlla e corregge il mismatch tra certificato e chiave privata ─────────
|
|
#
|
|
# Logica:
|
|
# 1. Cerca la chiave privata nello stesso percorso del certificato,
|
|
# oppure nelle posizioni canoniche di BastionGuard.
|
|
# 2. Confronta il modulus/pubkey del cert con quello della chiave.
|
|
# 3. Se combaciano → tutto OK, nessuna azione.
|
|
# 4. Se NON combaciano → rigenera SOLO il certificato (self-signed) usando
|
|
# la chiave esistente, mantenendo i metadati originali (CN, days).
|
|
# 5. Se la chiave non esiste affatto → genera ex-novo coppia chiave + cert.
|
|
#
|
|
check_and_fix_key_mismatch() {
|
|
log "Controllo mismatch chiave/certificato…"
|
|
|
|
# Se il certificato non esiste affatto, salta direttamente al CASO B
|
|
if [[ ! -f "$CA_SRC" ]]; then
|
|
warn "Certificato non trovato: $CA_SRC — genero nuova coppia chiave+certificato"
|
|
mkdir -p "$(dirname "$CA_SRC")"
|
|
fix_user_ca_permissions
|
|
local new_key_path
|
|
new_key_path="$(dirname "$CA_SRC")/$(basename "${CA_SRC%.crt.pem}").key.pem"
|
|
new_key_path="${new_key_path%.pem.pem}.pem"
|
|
local orig_subject="/CN=${CA_NAME}/O=BastionGuard/OU=Security"
|
|
if ! openssl genrsa -out "$new_key_path" 4096 >/dev/null 2>&1; then
|
|
err "Generazione chiave RSA fallita"; exit 1
|
|
fi
|
|
chmod 600 "$new_key_path"
|
|
[[ -n "${REAL_USER:-}" ]] && chown "$REAL_USER:$REAL_GROUP" "$new_key_path" 2>/dev/null || true
|
|
log "Nuova chiave privata: $new_key_path"
|
|
if openssl req -new -x509 -key "$new_key_path" -out "$CA_SRC" -days 3650 \
|
|
-subj "$orig_subject" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign,cRLSign" \
|
|
>/dev/null 2>&1; then
|
|
log "✅ Nuova coppia chiave+certificato generata (3650 giorni)"
|
|
else
|
|
local tmp_ext; tmp_ext="$(mktemp /tmp/ca_ext_XXXXXX.cnf)"
|
|
printf '[req]
|
|
distinguished_name = req_dn
|
|
x509_extensions = v3_ca
|
|
prompt = no
|
|
|
|
[req_dn]
|
|
CN = %s
|
|
O = BastionGuard
|
|
OU = Security
|
|
|
|
[v3_ca]
|
|
basicConstraints = critical, CA:TRUE
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
' "${CA_NAME}" > "$tmp_ext"
|
|
openssl req -new -x509 -key "$new_key_path" -out "$CA_SRC" -days 3650 -config "$tmp_ext" >/dev/null 2>&1 \
|
|
&& log "✅ Nuova coppia chiave+certificato generata (compat mode, 3650 giorni)" \
|
|
|| { err "Impossibile generare il certificato"; rm -f "$tmp_ext"; exit 1; }
|
|
rm -f "$tmp_ext"
|
|
fi
|
|
[[ -n "${REAL_USER:-}" ]] && chown "$REAL_USER:$REAL_GROUP" "$CA_SRC" 2>/dev/null || true
|
|
fix_user_ca_permissions
|
|
log "✅ Certificato generato, procedo con l'installazione"
|
|
return 0
|
|
fi
|
|
|
|
# ── Individua la chiave privata ──────────────────────────────────────────
|
|
local key_candidates=()
|
|
|
|
# 1. Stesso basename del cert, estensione .key o .key.pem
|
|
local base="${CA_SRC%.crt.pem}"
|
|
base="${base%.crt}"
|
|
base="${base%.pem}"
|
|
key_candidates+=( "${base}.key" "${base}.key.pem" )
|
|
|
|
# 2. Posizioni canoniche BastionGuard
|
|
local cert_dir
|
|
cert_dir="$(dirname "$CA_SRC")"
|
|
key_candidates+=(
|
|
"${cert_dir}/intercept-ca.key.pem"
|
|
"${cert_dir}/intercept-ca.key"
|
|
"${cert_dir}/${CA_NAME}.key.pem"
|
|
"${cert_dir}/${CA_NAME}.key"
|
|
"/etc/BastionGuard/certs/intercept-ca.key.pem"
|
|
"/etc/BastionGuard/certs/intercept-ca.key"
|
|
"/etc/BastionGuard/certs/${CA_NAME}.key.pem"
|
|
"/etc/BastionGuard/certs/${CA_NAME}.key"
|
|
)
|
|
|
|
# Se l'utente reale ha una home, aggiungi anche lì
|
|
if [[ -n "${REAL_HOME:-}" ]]; then
|
|
local ud="$REAL_HOME/.local/share/BastionGuard/certs"
|
|
key_candidates+=(
|
|
"${ud}/intercept-ca.key.pem"
|
|
"${ud}/intercept-ca.key"
|
|
"${ud}/${CA_NAME}.key.pem"
|
|
"${ud}/${CA_NAME}.key"
|
|
)
|
|
fi
|
|
|
|
local CA_KEY=""
|
|
for c in "${key_candidates[@]}"; do
|
|
if [[ -f "$c" ]]; then
|
|
CA_KEY="$c"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# ── Funzione interna: ottieni fingerprint pubkey di cert o key ───────────
|
|
_pubkey_fp_from_cert() { openssl x509 -in "$1" -noout -pubkey 2>/dev/null | openssl pkey -pubin -noout -text 2>/dev/null | sha256sum; }
|
|
_pubkey_fp_from_key() { openssl pkey -in "$1" -pubout 2>/dev/null | openssl pkey -pubin -noout -text 2>/dev/null | sha256sum; }
|
|
|
|
# ── CASO A: chiave trovata ───────────────────────────────────────────────
|
|
if [[ -n "$CA_KEY" ]]; then
|
|
log "Chiave privata trovata: $CA_KEY"
|
|
|
|
# Verifica che la chiave sia leggibile/valida
|
|
if ! openssl pkey -in "$CA_KEY" -noout >/dev/null 2>&1; then
|
|
warn "Chiave privata non valida o corrotta: $CA_KEY"
|
|
warn "Genero nuova coppia chiave+certificato"
|
|
CA_KEY="" # cade nel CASO B
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$CA_KEY" ]]; then
|
|
local fp_cert fp_key
|
|
fp_cert="$(_pubkey_fp_from_cert "$CA_SRC")"
|
|
fp_key="$( _pubkey_fp_from_key "$CA_KEY")"
|
|
|
|
if [[ "$fp_cert" == "$fp_key" ]]; then
|
|
fix_user_ca_permissions
|
|
log "✅ Chiave e certificato combaciano — nessun intervento necessario"
|
|
return 0
|
|
fi
|
|
|
|
warn "⚠️ MISMATCH rilevato: la chiave pubblica nel certificato NON corrisponde a $CA_KEY"
|
|
log "Rigenero il certificato usando la chiave esistente…"
|
|
|
|
# Leggo i metadati dall'attuale certificato
|
|
local subject days_left not_after now_ts exp_ts remaining_days
|
|
subject="$(openssl x509 -in "$CA_SRC" -noout -subject 2>/dev/null | sed 's/^subject=//')"
|
|
not_after="$(openssl x509 -in "$CA_SRC" -noout -enddate 2>/dev/null | cut -d= -f2)"
|
|
now_ts="$(date +%s)"
|
|
exp_ts="$(date -d "$not_after" +%s 2>/dev/null || python3 -c "import ssl,time; print(int(time.mktime(__import__('email.utils',fromlist=['parsedate']).parsedate('$not_after'))))" 2>/dev/null || echo 0)"
|
|
|
|
if [[ "$exp_ts" -gt "$now_ts" ]]; then
|
|
remaining_days=$(( (exp_ts - now_ts) / 86400 ))
|
|
# Proroga al massimo a 3650 gg se il residuo è troppo basso
|
|
[[ "$remaining_days" -lt 365 ]] && remaining_days=3650
|
|
else
|
|
remaining_days=3650
|
|
warn "Certificato scaduto — uso validità predefinita di 3650 giorni"
|
|
fi
|
|
|
|
# Backup del vecchio certificato
|
|
local backup="${CA_SRC}.bak.$(date +%Y%m%d%H%M%S)"
|
|
cp -a "$CA_SRC" "$backup"
|
|
log "Backup vecchio certificato: $backup"
|
|
|
|
# Genera nuovo certificato self-signed con la stessa chiave
|
|
if openssl req -new -x509 \
|
|
-key "$CA_KEY" \
|
|
-out "$CA_SRC" \
|
|
-days "$remaining_days" \
|
|
-subj "$subject" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign,cRLSign" \
|
|
>/dev/null 2>&1; then
|
|
log "✅ Certificato rigenerato con la chiave esistente ($remaining_days giorni)"
|
|
else
|
|
# Fallback: openssl < 1.1.1 non supporta -addext
|
|
local tmp_ext
|
|
tmp_ext="$(mktemp /tmp/ca_ext_XXXXXX.cnf)"
|
|
cat > "$tmp_ext" <<EOF
|
|
[req]
|
|
distinguished_name = req_dn
|
|
x509_extensions = v3_ca
|
|
prompt = no
|
|
|
|
[req_dn]
|
|
$(echo "$subject" | sed 's|/\([^=]*\)=|\1 = |g; s|^ ||')
|
|
|
|
[v3_ca]
|
|
basicConstraints = critical, CA:TRUE
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
EOF
|
|
openssl req -new -x509 \
|
|
-key "$CA_KEY" \
|
|
-out "$CA_SRC" \
|
|
-days "$remaining_days" \
|
|
-config "$tmp_ext" \
|
|
>/dev/null 2>&1 \
|
|
&& log "✅ Certificato rigenerato (compat mode, $remaining_days giorni)" \
|
|
|| { err "Impossibile rigenerare il certificato"; rm -f "$tmp_ext"; exit 1; }
|
|
rm -f "$tmp_ext"
|
|
fi
|
|
|
|
else
|
|
# ── CASO B: chiave non trovata — genera nuova coppia ─────────────────
|
|
warn "Nessuna chiave privata trovata per $CA_SRC"
|
|
log "Genero nuova coppia chiave RSA 4096 + certificato CA self-signed…"
|
|
|
|
# Scelgo il percorso di output per la nuova chiave
|
|
local new_key_path
|
|
new_key_path="$(dirname "$CA_SRC")/$(basename "${CA_SRC%.crt.pem}").key.pem"
|
|
new_key_path="${new_key_path%.pem.pem}.pem" # evita doppia estensione
|
|
|
|
local backup="${CA_SRC}.bak.$(date +%Y%m%d%H%M%S)"
|
|
[[ -f "$CA_SRC" ]] && { cp -a "$CA_SRC" "$backup"; log "Backup vecchio certificato: $backup"; }
|
|
|
|
# Cerca di mantenere il subject originale
|
|
local orig_subject="/CN=${CA_NAME}/O=BastionGuard/OU=Security"
|
|
if [[ -f "$backup" ]]; then
|
|
orig_subject="$(openssl x509 -in "$backup" -noout -subject 2>/dev/null | sed 's/^subject=//')" || true
|
|
fi
|
|
|
|
# Genera chiave privata
|
|
if ! openssl genrsa -out "$new_key_path" 4096 >/dev/null 2>&1; then
|
|
err "Generazione chiave RSA fallita"
|
|
exit 1
|
|
fi
|
|
chmod 600 "$new_key_path"
|
|
[[ -n "${REAL_USER:-}" ]] && chown "$REAL_USER:$REAL_GROUP" "$new_key_path" 2>/dev/null || true
|
|
log "Nuova chiave privata: $new_key_path"
|
|
|
|
# Genera certificato self-signed
|
|
if openssl req -new -x509 \
|
|
-key "$new_key_path" \
|
|
-out "$CA_SRC" \
|
|
-days 3650 \
|
|
-subj "$orig_subject" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign,cRLSign" \
|
|
>/dev/null 2>&1; then
|
|
log "✅ Nuova coppia chiave+certificato generata (3650 giorni)"
|
|
else
|
|
local tmp_ext
|
|
tmp_ext="$(mktemp /tmp/ca_ext_XXXXXX.cnf)"
|
|
cat > "$tmp_ext" <<EOF
|
|
[req]
|
|
distinguished_name = req_dn
|
|
x509_extensions = v3_ca
|
|
prompt = no
|
|
|
|
[req_dn]
|
|
$(echo "$orig_subject" | sed 's|/\([^=]*\)=|\1 = |g; s|^ ||')
|
|
|
|
[v3_ca]
|
|
basicConstraints = critical, CA:TRUE
|
|
keyUsage = critical, keyCertSign, cRLSign
|
|
subjectKeyIdentifier = hash
|
|
EOF
|
|
openssl req -new -x509 \
|
|
-key "$new_key_path" \
|
|
-out "$CA_SRC" \
|
|
-days 3650 \
|
|
-config "$tmp_ext" \
|
|
>/dev/null 2>&1 \
|
|
&& log "✅ Nuova coppia chiave+certificato generata (compat mode, 3650 giorni)" \
|
|
|| { err "Impossibile generare il certificato"; rm -f "$tmp_ext"; exit 1; }
|
|
rm -f "$tmp_ext"
|
|
fi
|
|
|
|
CA_KEY="$new_key_path"
|
|
fi
|
|
|
|
fix_user_ca_permissions
|
|
|
|
# Ri-valida dopo ogni intervento
|
|
if ! openssl x509 -in "$CA_SRC" -noout >/dev/null 2>&1; then
|
|
err "Il certificato rigenerato non è valido: $CA_SRC"
|
|
exit 1
|
|
fi
|
|
log "✅ Verifica post-fix superata"
|
|
}
|
|
|
|
# ── Helper Debian ─────────────────────────────────────────────────────────────
|
|
install_debian_style() {
|
|
local dst="$1"
|
|
local conf_entry="$2"
|
|
|
|
mkdir -p "$(dirname "$dst")"
|
|
|
|
rm -f \
|
|
/usr/local/share/ca-certificates/BastionGuard-ca.crt \
|
|
/usr/local/share/ca-certificates/BastionGuard-ca.pem \
|
|
/usr/local/share/ca-certificates/BastionGuard-CA.crt \
|
|
/usr/local/share/ca-certificates/BastionGuard-CA.pem \
|
|
/usr/share/ca-certificates/local/BastionGuard-ca.crt \
|
|
/usr/share/ca-certificates/local/BastionGuard-ca.pem \
|
|
/usr/share/ca-certificates/local/BastionGuard-CA.crt \
|
|
/usr/share/ca-certificates/local/BastionGuard-CA.pem
|
|
|
|
install -m 0644 "$CA_SRC" "$dst"
|
|
restore_selinux_context "$dst" "$(dirname "$dst")" /etc/ca-certificates /usr/share/ca-certificates /usr/local/share/ca-certificates
|
|
|
|
sed -i '/[Bb]astion[Gg]uard/d' /etc/ca-certificates.conf || true
|
|
echo "$conf_entry" >> /etc/ca-certificates.conf
|
|
|
|
log "Entry registrata in /etc/ca-certificates.conf: $conf_entry"
|
|
update-ca-certificates
|
|
}
|
|
|
|
|
|
# ── Policy SELinux locale per BastionGuard-cef.service ───────────────────────
|
|
# BastionGuard-cef.service è un servizio systemd utente, quindi gira come utente
|
|
# normale ma può comunque essere bloccato da SELinux su Fedora/RHEL mentre
|
|
# legge/scrive la propria CA e cache in home.
|
|
# Questa sezione crea una policy locale mirata al dominio SELinux effettivo
|
|
# dell'utente/processo e limita i permessi all'albero ~/.local/share/BastionGuard.
|
|
CEF_USER_SERVICE_NAME="BastionGuard-cef.service"
|
|
CEF_SERVICE_COMM="bastionguard-cef"
|
|
CEF_SERVICE_COMM_TRUNC="bastionguard-ce"
|
|
CEF_SERVICE_NAME_GLOB="BastionGuard-cef"
|
|
CEF_POLICY_MODULE="bastionguard_cef_local"
|
|
CEF_DATA_TYPE="bastionguard_cef_home_t"
|
|
|
|
regex_escape() {
|
|
printf '%s' "$1" | sed -E 's/[][().^$*+?{}|\\]/\\&/g'
|
|
}
|
|
|
|
selinux_type_from_context() {
|
|
awk -F: '{print $3}' <<<"$1"
|
|
}
|
|
|
|
selinux_type_name_valid() {
|
|
local t="$1"
|
|
[[ "$t" =~ ^[A-Za-z0-9_]+$ ]] || return 1
|
|
[[ "$t" == *_t ]] || return 1
|
|
}
|
|
|
|
detect_bastionguard_cef_source_domains() {
|
|
local d label comm rest raw svc_lc comm_lc rest_lc
|
|
svc_lc="${CEF_SERVICE_NAME_GLOB,,}"
|
|
|
|
# Dominio SELinux della sessione utente. Per un servizio systemd --user
|
|
# avviato come utente normale, spesso è lo stesso dominio del processo.
|
|
if [[ -n "${REAL_USER:-}" ]] && label="$(run_as_real_user id -Z 2>/dev/null || true)"; then
|
|
d="$(selinux_type_from_context "$label" 2>/dev/null || true)"
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
fi
|
|
|
|
# Dominio reale del processo BastionGuard-cef, se è già avviato.
|
|
# Nota: Linux tronca comm a 15 caratteri; bastionguard-cef può comparire
|
|
# come bastionguard-ce nelle AVC e in ps -o comm.
|
|
if have_cmd ps; then
|
|
while read -r label comm rest; do
|
|
[[ -n "${label:-}" ]] || continue
|
|
comm_lc="${comm,,}"
|
|
rest_lc="${rest,,}"
|
|
if [[ "$comm_lc" == *"${CEF_SERVICE_COMM,,}"* || \
|
|
"$comm_lc" == *"${CEF_SERVICE_COMM_TRUNC,,}"* || \
|
|
"$rest_lc" == *"${CEF_SERVICE_COMM,,}"* || \
|
|
"$rest_lc" == *"${svc_lc}"* ]]; then
|
|
d="$(selinux_type_from_context "$label" 2>/dev/null || true)"
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
fi
|
|
done < <(ps -eZ -o label=,comm=,args= 2>/dev/null || true)
|
|
fi
|
|
|
|
# Domini sorgente comparsi nelle denial recenti del servizio/processo.
|
|
if have_cmd ausearch; then
|
|
raw="$(
|
|
{
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM" 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM_TRUNC" 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent 2>/dev/null \
|
|
| grep -iE 'bastionguard-ce|BastionGuard-cef' || true
|
|
} | sort -u
|
|
)"
|
|
while IFS= read -r d; do
|
|
selinux_type_name_valid "$d" && printf '%s\n' "$d"
|
|
done < <(
|
|
printf '%s\n' "$raw" \
|
|
| grep -oE 'scontext=[^ ]+' \
|
|
| sed -E 's/^scontext=([^:]+:){2}([^:]+):.*/\2/' \
|
|
| sort -u || true
|
|
)
|
|
fi
|
|
}
|
|
|
|
write_bastionguard_cef_base_te() {
|
|
local te="$1"; shift
|
|
local domains=("$@")
|
|
local d
|
|
|
|
{
|
|
echo "module ${CEF_POLICY_MODULE} 1.0;"
|
|
echo
|
|
echo "require {"
|
|
for d in "${domains[@]}"; do
|
|
echo " type ${d};"
|
|
done
|
|
echo " type user_home_dir_t;"
|
|
echo " type user_home_t;"
|
|
echo " attribute file_type;"
|
|
echo " attribute user_home_type;"
|
|
echo " class dir { add_name create getattr ioctl lock open read remove_name rmdir search setattr write };"
|
|
echo " class file { append create getattr ioctl lock map open read rename setattr unlink write };"
|
|
echo " class lnk_file { create getattr read rename setattr unlink write };"
|
|
echo " class sock_file { create getattr setattr unlink write };"
|
|
echo " class fifo_file { create getattr ioctl lock open read setattr unlink write };"
|
|
echo "}"
|
|
echo
|
|
echo "type ${CEF_DATA_TYPE};"
|
|
echo "typeattribute ${CEF_DATA_TYPE} file_type;"
|
|
echo "typeattribute ${CEF_DATA_TYPE} user_home_type;"
|
|
echo
|
|
for d in "${domains[@]}"; do
|
|
echo "# Permessi sui dati BastionGuard dell'utente per ${d}."
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:dir { add_name create getattr ioctl lock open read remove_name rmdir search setattr write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:file { append create getattr ioctl lock map open read rename setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:lnk_file { create getattr read rename setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:sock_file { create getattr setattr unlink write };"
|
|
echo "allow ${d} ${CEF_DATA_TYPE}:fifo_file { create getattr ioctl lock open read setattr unlink write };"
|
|
echo "allow ${d} user_home_dir_t:dir { getattr open read search };"
|
|
echo "allow ${d} user_home_t:dir { getattr open read search };"
|
|
echo
|
|
done
|
|
} > "$te"
|
|
}
|
|
|
|
compile_and_install_selinux_te() {
|
|
local te="$1"
|
|
local base="$2"
|
|
checkmodule -M -m -o "${base}.mod" "$te"
|
|
semodule_package -o "${base}.pp" -m "${base}.mod"
|
|
semodule -i "${base}.pp"
|
|
}
|
|
|
|
install_bastionguard_cef_observed_avc_policy() {
|
|
# Facoltativo: se ci sono già denial recenti, crea un secondo modulo solo
|
|
# dalle AVC del servizio/processo BastionGuard-cef. È mirato alle denial reali osservate.
|
|
selinux_enabled || return 0
|
|
have_cmd ausearch || return 0
|
|
have_cmd audit2allow || return 0
|
|
|
|
local tmpd avc base
|
|
tmpd="$(mktemp -d /tmp/bastionguard-cef-avc.XXXXXX)"
|
|
avc="$tmpd/${CEF_SERVICE_COMM}.avc"
|
|
base="bastionguard_cef_observed"
|
|
|
|
{
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM" --raw 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent -c "$CEF_SERVICE_COMM_TRUNC" --raw 2>/dev/null || true
|
|
ausearch -m AVC,USER_AVC -ts recent --raw 2>/dev/null \
|
|
| grep -iE 'bastionguard-ce|BastionGuard-cef' || true
|
|
} | sort -u > "$avc"
|
|
|
|
if ! grep -qE 'avc:.*denied|type=AVC|type=USER_AVC' "$avc" 2>/dev/null; then
|
|
rm -rf "$tmpd"
|
|
log "Nessuna AVC denial recente per $CEF_USER_SERVICE_NAME / $CEF_SERVICE_COMM"
|
|
return 0
|
|
fi
|
|
|
|
if (
|
|
cd "$tmpd" && \
|
|
audit2allow -M "$base" -i "$avc" >/dev/null && \
|
|
semodule -i "${base}.pp"
|
|
); then
|
|
log "✅ Modulo SELinux da AVC recenti installato: ${base}"
|
|
else
|
|
warn "Impossibile generare/installare modulo da AVC recenti per $CEF_USER_SERVICE_NAME"
|
|
fi
|
|
rm -rf "$tmpd"
|
|
}
|
|
|
|
install_bastionguard_cef_selinux_policy() {
|
|
selinux_enabled || return 0
|
|
distro_is "fedora" || distro_is "rhel" || distro_is "centos" || return 0
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" && -n "${REAL_GROUP:-}" ]] || return 0
|
|
|
|
local missing=0 cmd
|
|
for cmd in checkmodule semodule_package semodule semanage restorecon; do
|
|
if ! have_cmd "$cmd"; then
|
|
warn "SELinux policy $CEF_USER_SERVICE_NAME: comando mancante: $cmd"
|
|
missing=1
|
|
fi
|
|
done
|
|
if [[ "$missing" -eq 1 ]]; then
|
|
warn "Fedora/RHEL: installare checkpolicy policycoreutils-python-utils policycoreutils per la policy di $CEF_USER_SERVICE_NAME"
|
|
return 0
|
|
fi
|
|
|
|
local domains_raw domains=() d tmpd te home_re data_re
|
|
domains_raw="$(detect_bastionguard_cef_source_domains | sort -u || true)"
|
|
if [[ -z "$domains_raw" ]]; then
|
|
warn "Nessun dominio SELinux rilevato per $CEF_USER_SERVICE_NAME; avvia il servizio utente una volta e rilancia lo script se SELinux blocca"
|
|
install_bastionguard_cef_observed_avc_policy
|
|
return 0
|
|
fi
|
|
|
|
while IFS= read -r d; do
|
|
[[ -n "$d" ]] && domains+=("$d")
|
|
done <<< "$domains_raw"
|
|
|
|
log "Domini SELinux per $CEF_USER_SERVICE_NAME: ${domains[*]}"
|
|
|
|
tmpd="$(mktemp -d /tmp/bastionguard-cef-selinux.XXXXXX)"
|
|
te="$tmpd/${CEF_POLICY_MODULE}.te"
|
|
write_bastionguard_cef_base_te "$te" "${domains[@]}"
|
|
|
|
if (
|
|
cd "$tmpd" && compile_and_install_selinux_te "$te" "$CEF_POLICY_MODULE"
|
|
); then
|
|
log "✅ Policy SELinux base installata: $CEF_POLICY_MODULE"
|
|
else
|
|
warn "Compilazione/installazione policy SELinux base fallita per $CEF_USER_SERVICE_NAME"
|
|
rm -rf "$tmpd"
|
|
install_bastionguard_cef_observed_avc_policy
|
|
return 0
|
|
fi
|
|
rm -rf "$tmpd"
|
|
|
|
home_re="$(regex_escape "$REAL_HOME")"
|
|
data_re="${home_re}/\.local/share/BastionGuard(/.*)?"
|
|
|
|
semanage fcontext -a -t "$CEF_DATA_TYPE" "$data_re" 2>/dev/null \
|
|
|| semanage fcontext -m -t "$CEF_DATA_TYPE" "$data_re" 2>/dev/null \
|
|
|| warn "Impossibile registrare fcontext SELinux per $REAL_HOME/.local/share/BastionGuard"
|
|
|
|
mkdir -p "$REAL_HOME/.local/share/BastionGuard"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$REAL_HOME/.local/share/BastionGuard" 2>/dev/null || true
|
|
chmod 700 "$REAL_HOME/.local/share/BastionGuard" 2>/dev/null || true
|
|
restore_selinux_context "$REAL_HOME/.local/share/BastionGuard"
|
|
|
|
install_bastionguard_cef_observed_avc_policy
|
|
|
|
log "✅ Policy SELinux $CEF_USER_SERVICE_NAME completata"
|
|
}
|
|
|
|
# ── Gestione servizio systemd utente BastionGuard-cef.service ────────────────
|
|
user_systemctl() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_UID:-}" ]] || return 1
|
|
[[ -d "/run/user/${REAL_UID}" ]] || return 1
|
|
run_as_real_user systemctl --user "$@"
|
|
}
|
|
|
|
bastionguard_cef_user_service_exists() {
|
|
local state=""
|
|
state="$(user_systemctl show "$CEF_USER_SERVICE_NAME" -p LoadState --value 2>/dev/null || true)"
|
|
[[ -n "$state" && "$state" != "not-found" ]] && return 0
|
|
user_systemctl status "$CEF_USER_SERVICE_NAME" >/dev/null 2>&1 && return 0
|
|
return 1
|
|
}
|
|
|
|
restart_bastionguard_cef_user_service_if_active() {
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_UID:-}" ]] || return 0
|
|
if ! bastionguard_cef_user_service_exists; then
|
|
log "Servizio utente $CEF_USER_SERVICE_NAME non trovato o bus utente non disponibile: skip restart"
|
|
return 0
|
|
fi
|
|
|
|
if user_systemctl is-active --quiet "$CEF_USER_SERVICE_NAME"; then
|
|
log "Riavvio servizio utente $CEF_USER_SERVICE_NAME per ricaricare CA/policy…"
|
|
if user_systemctl restart "$CEF_USER_SERVICE_NAME"; then
|
|
log "✅ Servizio utente riavviato: $CEF_USER_SERVICE_NAME"
|
|
else
|
|
warn "Restart di $CEF_USER_SERVICE_NAME fallito; prova manualmente: systemctl --user restart $CEF_USER_SERVICE_NAME"
|
|
fi
|
|
else
|
|
log "Servizio utente $CEF_USER_SERVICE_NAME non attivo: non lo avvio automaticamente"
|
|
fi
|
|
}
|
|
|
|
# ── 1. Installa nel trust store di sistema ────────────────────────────────────
|
|
check_and_fix_key_mismatch
|
|
fix_user_ca_permissions
|
|
close_running_browsers
|
|
purge_old_system_ca_copies
|
|
|
|
installed_sys=false
|
|
|
|
|
|
if distro_is "debian" || distro_is "ubuntu"; then
|
|
log "Rilevato: Debian/Ubuntu"
|
|
install_debian_style \
|
|
"/usr/share/ca-certificates/local/${CA_NAME}.crt" \
|
|
"local/${CA_NAME}.crt"
|
|
log "✅ CA installata (Debian/Ubuntu)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "fedora" || distro_is "rhel" || distro_is "centos"; then
|
|
log "Rilevato: Fedora/RHEL/CentOS"
|
|
DST="/etc/pki/ca-trust/source/anchors/${CA_NAME}.pem"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
# Su Fedora/RHEL il label SELinux errato su /etc/pki/ca-trust può impedire
|
|
# lettura/estrazione corretta in ambienti enforcing. Ripristina prima e dopo.
|
|
restore_selinux_context "$DST" "$(dirname "$DST")" /etc/pki/ca-trust
|
|
update-ca-trust extract
|
|
fix_selinux_system_trust_contexts
|
|
log "✅ CA installata (Fedora/RHEL)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "arch" || distro_is "archlinux"; then
|
|
log "Rilevato: Arch Linux"
|
|
DST="/etc/ca-certificates/trust-source/anchors/${CA_NAME}.crt"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
trust extract-compat
|
|
log "✅ CA installata (Arch Linux)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "opensuse" || distro_is "suse"; then
|
|
log "Rilevato: openSUSE/SLES"
|
|
DST="/etc/pki/trust/anchors/${CA_NAME}.pem"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
update-ca-certificates
|
|
log "✅ CA installata (openSUSE)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "alpine"; then
|
|
log "Rilevato: Alpine Linux"
|
|
DST="/usr/local/share/ca-certificates/${CA_NAME}.crt"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
update-ca-certificates
|
|
log "✅ CA installata (Alpine)"
|
|
installed_sys=true
|
|
|
|
elif distro_is "void"; then
|
|
log "Rilevato: Void Linux"
|
|
DST="/usr/share/ca-certificates/bastionguard/${CA_NAME}.pem"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
CONF="/etc/ca-certificates/update.d/bastionguard.conf"
|
|
mkdir -p "$(dirname "$CONF")"
|
|
echo "bastionguard/${CA_NAME}.pem" > "$CONF"
|
|
update-ca-certificates
|
|
log "✅ CA installata (Void Linux)"
|
|
installed_sys=true
|
|
fi
|
|
|
|
if ! $installed_sys; then
|
|
warn "Distro non riconosciuta (ID='${ID:-?}', ID_LIKE='${ID_LIKE:-?}') — fallback"
|
|
|
|
if have_cmd trust; then
|
|
DST="/etc/ca-certificates/trust-source/anchors/${CA_NAME}.crt"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
trust extract-compat
|
|
log "✅ CA installata (fallback: trust)"
|
|
installed_sys=true
|
|
|
|
elif have_cmd update-ca-trust; then
|
|
DST="/etc/pki/ca-trust/source/anchors/${CA_NAME}.pem"
|
|
mkdir -p "$(dirname "$DST")"
|
|
install -m 0644 "$CA_SRC" "$DST"
|
|
restore_selinux_context "$DST" "$(dirname "$DST")" /etc/pki/ca-trust
|
|
update-ca-trust extract
|
|
fix_selinux_system_trust_contexts
|
|
log "✅ CA installata (fallback: update-ca-trust)"
|
|
installed_sys=true
|
|
|
|
elif have_cmd update-ca-certificates; then
|
|
install_debian_style \
|
|
"/usr/share/ca-certificates/local/${CA_NAME}.crt" \
|
|
"local/${CA_NAME}.crt"
|
|
log "✅ CA installata (fallback: update-ca-certificates)"
|
|
installed_sys=true
|
|
fi
|
|
fi
|
|
|
|
if ! $installed_sys; then
|
|
err "Nessun metodo di installazione trovato per questa distro"
|
|
exit 1
|
|
fi
|
|
|
|
# Ripristina label SELinux sui trust store toccati, senza rendere obbligatorio SELinux.
|
|
fix_selinux_system_trust_contexts
|
|
fix_user_ca_permissions
|
|
|
|
# ── validate_ca dopo installazione sistema ───────────────────────────────────
|
|
validate_ca
|
|
|
|
# ── 2. NSS system-wide ────────────────────────────────────────────────────────
|
|
if have_cmd certutil; then
|
|
for db in /etc/pki/nssdb /usr/share/pki/nssdb; do
|
|
[ -d "$db" ] || continue
|
|
prefix="dbm:"
|
|
[ -f "$db/cert9.db" ] && prefix="sql:"
|
|
nss_delete_matching_nicknames_root "${prefix}${db}"
|
|
certutil -A -d "${prefix}${db}" -n "$CA_NAME" -t "CT,," -i "$CA_SRC" 2>/dev/null \
|
|
&& log "✅ NSS system: $db" \
|
|
|| warn "NSS system $db: certutil fallito (non fatale)"
|
|
restore_selinux_context "$db"
|
|
done
|
|
else
|
|
warn "certutil non trovato — NSS system-wide non aggiornato"
|
|
fi
|
|
|
|
# ── 3. NSS utente ─────────────────────────────────────────────────────────────
|
|
nss_db_prefix() {
|
|
[[ -f "$1/cert9.db" ]] && printf 'sql:%s' "$1" || printf 'dbm:%s' "$1"
|
|
}
|
|
|
|
import_nss_db() {
|
|
local db="$1"
|
|
[[ -d "$db" ]] || return 0
|
|
[[ -f "$db/cert9.db" || -f "$db/cert8.db" ]] || return 0
|
|
|
|
local prefix
|
|
prefix="$(nss_db_prefix "$db")"
|
|
|
|
nss_delete_matching_nicknames_user "$prefix"
|
|
if run_as_real_user certutil -A -d "$prefix" -n "$CA_NAME" -t "CT,," -i "$CA_SRC" >/dev/null 2>&1; then
|
|
log "✅ NSS utente: $db"
|
|
else
|
|
warn "NSS utente $db: import fallito (non fatale)"
|
|
fi
|
|
[[ -n "${REAL_USER:-}" && -n "${REAL_GROUP:-}" ]] && chown -R "$REAL_USER:$REAL_GROUP" "$db" 2>/dev/null || true
|
|
restore_selinux_context "$db"
|
|
}
|
|
|
|
ensure_nssdb() {
|
|
local db="$1"
|
|
[[ -n "${REAL_USER:-}" ]] || return 0
|
|
|
|
if [[ ! -d "$db" ]]; then
|
|
log "Creo directory NSS DB: $db"
|
|
mkdir -p "$db"
|
|
chown "$REAL_USER:$REAL_GROUP" "$db"
|
|
chmod 700 "$db"
|
|
restore_selinux_context "$db"
|
|
fi
|
|
|
|
if [[ ! -f "$db/cert9.db" && ! -f "$db/cert8.db" ]]; then
|
|
log "Inizializzo NSS DB: $db"
|
|
if run_as_real_user certutil -N -d "sql:$db" --empty-password 2>/dev/null; then
|
|
log "✅ NSS DB creato: $db"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$db"
|
|
restore_selinux_context "$db"
|
|
else
|
|
if run_as_real_user certutil -N -d "dbm:$db" --empty-password 2>/dev/null; then
|
|
log "✅ NSS DB (dbm) creato: $db"
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$db"
|
|
restore_selinux_context "$db"
|
|
else
|
|
warn "Impossibile creare NSS DB: $db"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
import_firefox_profile_dir() {
|
|
local prof="$1"
|
|
[[ -d "$prof" ]] || return 0
|
|
|
|
if [[ ! -f "$prof/cert9.db" && ! -f "$prof/cert8.db" ]]; then
|
|
log "Inizializzo NSS DB nel profilo Firefox: $prof"
|
|
if run_as_real_user certutil -N -d "sql:$prof" --empty-password >/dev/null 2>&1; then
|
|
:
|
|
elif run_as_real_user certutil -N -d "dbm:$prof" --empty-password >/dev/null 2>&1; then
|
|
:
|
|
else
|
|
warn "Impossibile inizializzare NSS DB nel profilo: $prof"
|
|
return 1
|
|
fi
|
|
chown -R "$REAL_USER:$REAL_GROUP" "$prof" 2>/dev/null || true
|
|
restore_selinux_context "$prof"
|
|
fi
|
|
|
|
import_nss_db "$prof"
|
|
}
|
|
|
|
firefox_imported_seen=""
|
|
|
|
already_imported_firefox_profile() {
|
|
local prof="$1"
|
|
case "
|
|
$firefox_imported_seen
|
|
" in
|
|
*"
|
|
$prof
|
|
"*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
mark_imported_firefox_profile() {
|
|
local prof="$1"
|
|
firefox_imported_seen="${firefox_imported_seen}
|
|
$prof"
|
|
}
|
|
|
|
import_firefox_profile_once() {
|
|
local prof="$1"
|
|
[[ -n "$prof" ]] || return 0
|
|
[[ -d "$prof" ]] || return 0
|
|
|
|
if already_imported_firefox_profile "$prof"; then
|
|
return 0
|
|
fi
|
|
|
|
mark_imported_firefox_profile "$prof"
|
|
import_firefox_profile_dir "$prof"
|
|
}
|
|
|
|
import_firefox_root() {
|
|
local root="$1"
|
|
local found=0
|
|
local ini="$root/profiles.ini"
|
|
[[ -d "$root" ]] || return 0
|
|
|
|
firefox_imported_seen=""
|
|
|
|
log "Scansione profili Firefox in: $root"
|
|
|
|
# 1. Sezioni [Install...] con Default=...
|
|
if [[ -f "$ini" ]]; then
|
|
while IFS= read -r relpath; do
|
|
[[ -n "$relpath" ]] || continue
|
|
local prof="$root/$relpath"
|
|
if [[ -d "$prof" ]]; then
|
|
found=1
|
|
log "Profilo Firefox da sezione [Install*] Default=: $prof"
|
|
import_firefox_profile_once "$prof"
|
|
fi
|
|
done < <(
|
|
awk -F= '
|
|
BEGIN { in_install=0 }
|
|
/^\[Install[^]]*\]/ { in_install=1; next }
|
|
/^\[/ { in_install=0; next }
|
|
in_install && $1=="Default" {
|
|
gsub(/\r/,"",$2)
|
|
print $2
|
|
}
|
|
' "$ini" 2>/dev/null || true
|
|
)
|
|
fi
|
|
|
|
# 2. Profili [ProfileN] con Default=1
|
|
if [[ -f "$ini" ]]; then
|
|
while IFS='|' read -r is_relative path; do
|
|
[[ -n "$path" ]] || continue
|
|
local prof=""
|
|
if [[ "$is_relative" == "1" ]]; then
|
|
prof="$root/$path"
|
|
else
|
|
prof="$path"
|
|
fi
|
|
if [[ -d "$prof" ]]; then
|
|
found=1
|
|
log "Profilo Firefox da profiles.ini (Default=1): $prof"
|
|
import_firefox_profile_once "$prof"
|
|
fi
|
|
done < <(
|
|
awk -F= -v ROOT="$root" '
|
|
BEGIN {
|
|
in_profile=0
|
|
is_default=0
|
|
path=""
|
|
is_relative=1
|
|
}
|
|
|
|
/^\[Profile[0-9]+\]/ {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
in_profile=1
|
|
is_default=0
|
|
path=""
|
|
is_relative=1
|
|
next
|
|
}
|
|
|
|
/^\[/ {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
in_profile=0
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="Default" {
|
|
gsub(/\r/,"",$2)
|
|
is_default=$2
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="Path" {
|
|
gsub(/\r/,"",$2)
|
|
path=$2
|
|
next
|
|
}
|
|
|
|
in_profile && $1=="IsRelative" {
|
|
gsub(/\r/,"",$2)
|
|
is_relative=$2
|
|
next
|
|
}
|
|
|
|
END {
|
|
if (in_profile && is_default == 1 && path != "") {
|
|
printf "%s|%s\n", is_relative, path
|
|
}
|
|
}
|
|
' "$ini" 2>/dev/null || true
|
|
)
|
|
fi
|
|
|
|
# 3. Forza tutti i *.default-release
|
|
while IFS= read -r -d '' prof; do
|
|
found=1
|
|
log "Profilo Firefox forzato (*.default-release): $prof"
|
|
import_firefox_profile_once "$prof"
|
|
done < <(
|
|
find "$root" -mindepth 1 -maxdepth 1 -type d -name '*.default-release' -print0 2>/dev/null || true
|
|
)
|
|
|
|
# 4. Fallback finale su profili comuni
|
|
while IFS= read -r -d '' prof; do
|
|
found=1
|
|
log "Profilo Firefox fallback: $prof"
|
|
import_firefox_profile_once "$prof"
|
|
done < <(
|
|
find "$root" -mindepth 1 -maxdepth 1 -type d \
|
|
\( \
|
|
-name '*.default' -o \
|
|
-name '*.default-esr' -o \
|
|
-name '*.default-release' -o \
|
|
-name '*.profile' -o \
|
|
-name '*.release' -o \
|
|
-name '*.esr' \
|
|
\) \
|
|
-print0 2>/dev/null || true
|
|
)
|
|
|
|
if [[ "$found" -eq 0 ]]; then
|
|
warn "Nessun profilo Firefox trovato in: $root"
|
|
fi
|
|
}
|
|
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
close_running_browsers
|
|
log "Aggiornamento NSS utente per: $REAL_USER"
|
|
|
|
ensure_nssdb "$REAL_HOME/.pki/nssdb"
|
|
import_nss_db "$REAL_HOME/.pki/nssdb"
|
|
|
|
# Firefox
|
|
import_firefox_root "$REAL_HOME/.mozilla/firefox"
|
|
import_firefox_root "$REAL_HOME/snap/firefox/common/.mozilla/firefox"
|
|
import_firefox_root "$REAL_HOME/.var/app/org.mozilla.firefox/.mozilla/firefox"
|
|
|
|
# Fork Firefox-like (nativi)
|
|
import_firefox_root "$REAL_HOME/.floorp"
|
|
import_firefox_root "$REAL_HOME/.librewolf"
|
|
import_firefox_root "$REAL_HOME/.waterfox"
|
|
import_firefox_root "$REAL_HOME/.zen"
|
|
import_firefox_root "$REAL_HOME/.mullvad"
|
|
import_firefox_root "$REAL_HOME/.ghostery"
|
|
import_firefox_root "$REAL_HOME/.basilisk"
|
|
import_firefox_root "$REAL_HOME/.moonchild productions/pale moon"
|
|
import_firefox_root "$REAL_HOME/.config/floorp"
|
|
import_firefox_root "$REAL_HOME/.config/librewolf"
|
|
|
|
# Fork Firefox-like (flatpak)
|
|
import_firefox_root "$REAL_HOME/.var/app/io.gitlab.librewolf-community/.librewolf"
|
|
import_firefox_root "$REAL_HOME/.var/app/net.waterfox.waterfox/.waterfox"
|
|
import_firefox_root "$REAL_HOME/.var/app/one.ablaze.floorp/.floorp"
|
|
import_firefox_root "$REAL_HOME/.var/app/com.mullvad.MullvadBrowser/.mullvad"
|
|
|
|
# Browser snap Chromium-based / affini
|
|
for _snap_nssdb in \
|
|
"$REAL_HOME/snap/chromium/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/chromium/common/.pki/nssdb" \
|
|
"$REAL_HOME/snap/brave/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/microsoft-edge/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/opera/current/.pki/nssdb" \
|
|
"$REAL_HOME/snap/vivaldi/current/.pki/nssdb" \
|
|
; do
|
|
ensure_nssdb "$_snap_nssdb"
|
|
import_nss_db "$_snap_nssdb"
|
|
done
|
|
|
|
# Browser Chromium-based nativi — tutti usano ~/.pki/nssdb (già importato sopra)
|
|
# ma alcuni hanno il proprio nssdb separato
|
|
for _chrome_cfg in \
|
|
"$REAL_HOME/.config/google-chrome" \
|
|
"$REAL_HOME/.config/google-chrome-beta" \
|
|
"$REAL_HOME/.config/google-chrome-unstable" \
|
|
"$REAL_HOME/.config/chromium" \
|
|
"$REAL_HOME/.config/chromium-browser" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser-Beta" \
|
|
"$REAL_HOME/.config/BraveSoftware/Brave-Browser-Nightly" \
|
|
"$REAL_HOME/.config/vivaldi" \
|
|
"$REAL_HOME/.config/vivaldi-snapshot" \
|
|
"$REAL_HOME/.config/opera" \
|
|
"$REAL_HOME/.config/opera-beta" \
|
|
"$REAL_HOME/.config/opera-developer" \
|
|
"$REAL_HOME/.config/microsoft-edge" \
|
|
"$REAL_HOME/.config/microsoft-edge-beta" \
|
|
"$REAL_HOME/.config/microsoft-edge-dev" \
|
|
"$REAL_HOME/.config/thorium" \
|
|
"$REAL_HOME/.config/Thorium" \
|
|
"$REAL_HOME/.config/ungoogled-chromium" \
|
|
"$REAL_HOME/.config/yandex-browser" \
|
|
"$REAL_HOME/.config/epiphany" \
|
|
; do
|
|
[[ -d "$_chrome_cfg" ]] || continue
|
|
while IFS= read -r -d '' p; do
|
|
ensure_nssdb "$p"
|
|
import_nss_db "$p"
|
|
done < <(find "$_chrome_cfg" -type d -name nssdb -print0 2>/dev/null || true)
|
|
done
|
|
|
|
# Chromium / Chrome / Brave / Vivaldi / Edge / Opera flatpak
|
|
for _flatpak_id in \
|
|
org.chromium.Chromium \
|
|
com.google.Chrome \
|
|
com.google.ChromeDev \
|
|
com.brave.Browser \
|
|
com.vivaldi.Vivaldi \
|
|
com.opera.Opera \
|
|
com.microsoft.Edge \
|
|
io.github.ungoogled_software.ungoogled_chromium \
|
|
com.github.Eloston.UngoogledChromium \
|
|
org.gnome.Epiphany \
|
|
; do
|
|
if [[ -d "$REAL_HOME/.var/app/${_flatpak_id}" ]]; then
|
|
ensure_nssdb "$REAL_HOME/.var/app/${_flatpak_id}/.pki/nssdb"
|
|
import_nss_db "$REAL_HOME/.var/app/${_flatpak_id}/.pki/nssdb"
|
|
fi
|
|
while IFS= read -r -d '' p; do
|
|
ensure_nssdb "$p"
|
|
import_nss_db "$p"
|
|
done < <(find "$REAL_HOME/.var/app/${_flatpak_id}" -type d -name nssdb -print0 2>/dev/null || true)
|
|
done
|
|
|
|
fix_selinux_user_browser_contexts
|
|
else
|
|
warn "certutil non disponibile o utente reale non determinato — NSS utente saltato"
|
|
fi
|
|
|
|
# ── 4. Step finale di sicurezza ───────────────────────────────────────────────
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
FINAL_NSS_DB="$REAL_HOME/.pki/nssdb"
|
|
|
|
log "Step finale di sicurezza su NSS utente: $FINAL_NSS_DB"
|
|
ensure_nssdb "$FINAL_NSS_DB"
|
|
|
|
nss_delete_matching_nicknames_user "sql:$FINAL_NSS_DB"
|
|
|
|
if run_as_real_user certutil -A \
|
|
-d "sql:$FINAL_NSS_DB" \
|
|
-n "$CA_NAME" \
|
|
-t "CT,," \
|
|
-i "$CA_SRC" >/dev/null 2>&1; then
|
|
log "✅ Step finale NSS utente completato"
|
|
else
|
|
warn "Step finale NSS utente fallito in sql:, provo dbm:"
|
|
nss_delete_matching_nicknames_user "dbm:$FINAL_NSS_DB"
|
|
if run_as_real_user certutil -A \
|
|
-d "dbm:$FINAL_NSS_DB" \
|
|
-n "$CA_NAME" \
|
|
-t "CT,," \
|
|
-i "$CA_SRC" >/dev/null 2>&1; then
|
|
log "✅ Step finale NSS utente completato (dbm)"
|
|
else
|
|
warn "Step finale NSS utente fallito anche in dbm:"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
fix_selinux_user_browser_contexts
|
|
|
|
# ── 5. Verifica finale ────────────────────────────────────────────────────────
|
|
if have_cmd update-ca-certificates && [[ -d /etc/ssl/certs ]]; then
|
|
hash_val="$(openssl x509 -in "$CA_SRC" -noout -hash 2>/dev/null || true)"
|
|
if [[ -n "$hash_val" ]] && ls "/etc/ssl/certs/${hash_val}".* >/dev/null 2>&1; then
|
|
log "✅ Verifica bundle OK: /etc/ssl/certs/${hash_val}.0 presente"
|
|
else
|
|
warn "Hash non trovato in /etc/ssl/certs — verifica: ls /etc/ssl/certs/${hash_val}.*"
|
|
fi
|
|
fi
|
|
|
|
if have_cmd certutil && [[ -n "${REAL_USER:-}" && -n "${REAL_HOME:-}" ]]; then
|
|
if run_as_real_user certutil -L -d "sql:$REAL_HOME/.pki/nssdb" -n "$CA_NAME" >/dev/null 2>&1; then
|
|
log "✅ Verifica NSS utente OK: $REAL_HOME/.pki/nssdb"
|
|
else
|
|
warn "Verifica NSS utente non riuscita su sql:$REAL_HOME/.pki/nssdb"
|
|
fi
|
|
fi
|
|
|
|
install_bastionguard_cef_selinux_policy
|
|
fix_user_ca_permissions
|
|
fix_selinux_system_trust_contexts
|
|
fix_selinux_user_browser_contexts
|
|
restart_bastionguard_cef_user_service_if_active
|
|
log "✅ Installazione completata"
|
|
exit 0
|