--- /dev/null
+#!/usr/bin/env bash
+# Samba NT4 + OpenLDAP — Domain Health Check (v1.4 FULL)
+# Autore: Copilot per Giorgio Ravera
+# Uso: sudo ./domain_health_check_v1.4.sh
+# Note: esegue controlli su SID, utenti, gruppi, LDAP e mapping Samba.
+
+set -uo pipefail
+
+# ---- Colori & contatori ----
+GREEN="\e[32m"; YELLOW="\e[33m"; RED="\e[31m"; CYAN="\e[36m"; BOLD="\e[1m"; RESET="\e[0m"
+pass_n=0; warn_n=0; fail_n=0
+ok() { echo -e "${GREEN}[OK]${RESET} $*"; ((pass_n++)) || true; }
+warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; ((warn_n++)) || true; }
+fail() { echo -e "${RED}[FAIL]${RESET} $*"; ((fail_n++)) || true; }
+info() { echo -e "${CYAN}[*]${RESET} $*"; }
+hr() { echo -e "${BOLD}------------------------------------------------------------${RESET}"; }
+
+if [[ $(id -u) -ne 0 ]]; then
+ warn "Esegui come root per ottenere tutti i risultati (alcuni comandi richiedono privilegi).";
+fi
+
+# Comandi utili (non tutti obbligatori)
+CMDS=(net pdbedit getent awk sed grep cut sort uniq slapcat ldapsearch testparm id)
+for c in "${CMDS[@]}"; do command -v "$c" >/dev/null 2>&1 || warn "Comando non trovato: $c (alcuni check potrebbero essere saltati)"; done
+
+# --- CHECKS ---
+check_sids() {
+ hr; info "Controllo SID di dominio vs SID locale"
+ command -v net >/dev/null 2>&1 || { warn "net non disponibile"; return; }
+ local local_sid domain_sid
+ local_sid=$(net getlocalsid 2>/dev/null | awk -F': ' '{print $2}' | tr -d '\r\n')
+ domain_sid=$(net getdomainsid 2>/dev/null | awk -F': ' '{print $2}' | tr -d '\r\n')
+ [[ -n "$local_sid" ]] && info "Local SID: $local_sid" || warn "Impossibile leggere Local SID"
+ [[ -n "$domain_sid" ]] && info "Domain SID: $domain_sid" || warn "Impossibile leggere Domain SID"
+ [[ -n "$local_sid" && -n "$domain_sid" ]] && {
+ [[ "$local_sid" == "$domain_sid" ]] && ok "SID locale e di dominio coincidono" || fail "SID locale e di dominio NON coincidono"; }
+}
+
+check_admin_500_512() {
+ hr; info "Verifica Administrator (RID 500) e Primary Group (512)"
+ command -v pdbedit >/dev/null 2>&1 || { warn "pdbedit non disponibile"; return; }
+ if pdbedit -L | cut -d: -f1 | grep -qx "admin"; then
+ local out; out=$(pdbedit -L -v admin 2>/dev/null)
+ echo "$out" | grep -Eq "User SID: .*-500$" && ok "admin ha RID 500" || fail "admin NON ha RID 500"
+ echo "$out" | grep -Eq "Primary Group SID: .*-512$" && ok "admin ha Primary Group 512 (Domain Admins)" || fail "admin NON ha Primary Group 512"
+ else
+ warn "Utente 'admin' non trovato, cerco chi ha RID 500"
+ local u; u=$(pdbedit -L -v 2>/dev/null | awk '/User SID:/ {sid=$3} /Unix username:/ {user=$3} {if (sid ~ /-500$/) print user}' | head -n1)
+ [[ -n "$u" ]] && ok "Trovato utente con RID 500: $u" || fail "Nessun utente con RID 500 trovato"
+ fi
+}
+
+check_admin_membership() {
+ hr; info "Verifica membership effettiva di 'admin' in Domain Admins (gid 512)"
+ command -v id >/dev/null 2>&1 || { warn "id non disponibile"; return; }
+ if id admin >/dev/null 2>&1; then
+ id admin | grep -Eq 'gid=512\(| 512\(' && ok "'admin' ha gid primario 512 (Domain Admins) oppure è membro di 512" || warn "'admin' non appare con gid/membro 512 nell'output di id"
+ else
+ warn "Utente 'admin' non risolto da id(1)"
+ fi
+}
+
+check_low_rids() {
+ hr; info "Verifica RIDs bassi indesiderati (<1000, esclusi 500 e 501)"
+ command -v pdbedit >/dev/null 2>&1 || { warn "pdbedit non disponibile"; return; }
+ local bad; bad=$(pdbedit -L -v 2>/dev/null | awk '/User SID:/ {sid=$3; split(sid,a,"-"); rid=a[length(a)]; if (rid+0 < 1000 && rid != 500 && rid != 501) print sid}')
+ [[ -z "$bad" ]] && ok "Nessun utente con RID anomalo basso" || fail "RIDs bassi trovati:\n$bad"
+}
+
+check_guest_501() {
+ hr; info "Verifica account Guest (RID 501)"
+ command -v pdbedit >/dev/null 2>&1 || { warn "pdbedit non disponibile"; return; }
+ # Usa AWK per evitare problemi con '$' nei pattern
+ local found
+ found=$(pdbedit -L -v 2>/dev/null | awk '
+ /^Unix username:/ {user=$3}
+ /^User SID:/ {sid=$3; n=split(sid,a,"-"); rid=a[n]; if (rid==501) {print user ":" sid}}
+ ')
+ if [[ -n "$found" ]]; then
+ ok "Account Guest trovato: $found"
+ else
+ warn "RID 501 non trovato. (Inusuale per NT4, verifica configurazione)"
+ fi
+}
+
+check_groupmap_core() {
+ hr; info "Verifica groupmap core (512, 513, 514, 515)"
+ command -v net >/dev/null 2>&1 || { warn "net non disponibile"; return; }
+ local gm; gm=$(net groupmap list 2>/dev/null)
+ [[ -z "$gm" ]] && { warn "net groupmap list non ha restituito output"; return; }
+ echo "$gm" | grep -Fq -- "-512)" && ok "Domain Admins (512) presente" || fail "Domain Admins (512) mancante"
+ echo "$gm" | grep -Fq -- "-513)" && ok "Domain Users (513) presente" || fail "Domain Users (513) mancante"
+ echo "$gm" | grep -Fq -- "-514)" && ok "Domain Guests (514) presente" || fail "Domain Guests (514) mancante"
+ echo "$gm" | grep -Fq -- "-515)" && ok "Domain Computers (515) presente" || fail "Domain Computers (515) mancante"
+}
+
+check_group_name_collisions() {
+ hr; info "Verifica collisioni di NOME gruppo tra sorgenti (files/ldap)"
+ command -v getent >/dev/null 2>&1 || { warn "getent non disponibile"; return; }
+ local dups; dups=$(getent group | cut -d: -f1 | sort | uniq -d)
+ [[ -z "$dups" ]] && ok "Nessuna collisione di nome gruppo rilevata" || warn "Collisioni nomi gruppo rilevate:\n$dups"
+}
+
+check_nobody_guest_split() {
+ hr; info "Verifica separazione nobody (locale) vs guest (LDAP)"
+ command -v getent >/dev/null 2>&1 || { warn "getent non disponibile"; return; }
+ local nb ge; nb=$(getent passwd nobody); ge=$(getent passwd guest)
+ [[ -n "$nb" ]] && ok "nobody locale presente: $(echo "$nb" | cut -d: -f1,3,4,7)" || warn "nobody locale non trovato"
+ if [[ -n "$ge" ]]; then
+ local uid gid; uid=$(echo "$ge" | cut -d: -f3); gid=$(echo "$ge" | cut -d: -f4)
+ [[ "$uid" == "65534" && "$gid" == "514" ]] && ok "guest LDAP coerente (uid=65534, gid=514)" || warn "guest LDAP anomalo (atteso uid=65534,gid=514): $(echo "$ge" | cut -d: -f1,3,4,7)"
+ else
+ warn "guest LDAP non trovato"
+ fi
+}
+
+check_ldap_domain_objects() {
+ hr; info "Verifica oggetti sambaDomain in LDAP"
+ if command -v slapcat >/dev/null 2>&1; then
+ local count sid arid
+ count=$(slapcat -n 1 2>/dev/null | grep -c "^objectClass: sambaDomain")
+ sid=$(slapcat -n 1 2>/dev/null | awk -F': ' '/^sambaSID:/ {print $2}' | sort -u)
+ arid=$(slapcat -n 1 2>/dev/null | awk -F': ' '/^sambaAlgorithmicRidBase:/ {print $2}' | tr -d '\r\n')
+ info "sambaDomain objects: $count"
+ info "sambaSID (dominio) trovato/i: $(echo "$sid" | tr '\n' ' ')"
+ [[ "$count" -eq 1 ]] && ok "Unico oggetto sambaDomain presente" || warn "Attesi 1 sambaDomain, trovati $count"
+ [[ -n "$arid" ]] && ok "sambaAlgorithmicRidBase=$arid" || warn "sambaAlgorithmicRidBase non trovato"
+ else
+ warn "slapcat non disponibile: salto controlli oggetto sambaDomain"
+ fi
+}
+
+check_machine_accounts() {
+ hr; info "Verifica account macchina (uid che terminano con $) con primary group 515"
+ command -v slapcat >/dev/null 2>&1 || { warn "slapcat non disponibile"; return; }
+ local bad
+ bad=$(slapcat -n 1 2>/dev/null | awk '/^dn: uid=.*\$,/ {inhost=1; uid=$2} /^sambaPrimaryGroupSID:/ {if (inhost){pg=$2; if (pg !~ /-515$/) print uid" -> "pg; inhost=0}}')
+ [[ -z "$bad" ]] && ok "Tutti gli account macchina hanno primary group 515 (Domain Computers)" || warn "Account macchina con primary group non 515:\n$bad"
+}
+
+check_testparm() {
+ hr; info "Validazione configurazione Samba con testparm"
+ command -v testparm >/dev/null 2>&1 || { warn "testparm non disponibile"; return; }
+ if testparm -s >/dev/null 2>&1; then
+ ok "testparm OK (configurazione Samba valida)"
+ else
+ fail "testparm ha rilevato errori nella configurazione"
+ testparm -s 2>&1 | sed -n '1,120p'
+ fi
+}
+
+summary() {
+ hr; echo -e "${BOLD}Riepilogo:${RESET} PASS=$pass_n WARN=$warn_n FAIL=$fail_n"; [[ $fail_n -eq 0 ]] && exit 0 || exit 1
+}
+
+main() {
+ echo -e "${BOLD}Samba NT4 + OpenLDAP — Health Check (v1.4 FULL)${RESET}"
+ check_sids
+ check_admin_500_512
+ check_admin_membership
+ check_low_rids
+ check_guest_501
+ check_groupmap_core
+ check_group_name_collisions
+ check_nobody_guest_split
+ check_ldap_domain_objects
+ check_machine_accounts
+ check_testparm
+ summary
+}
+
+main "$@"