#!/usr/bin/env bash
# ============================================================
# HalaVoice Security Daemon — Background continuous monitoring
#
# Monitors:
#   1. File integrity (critical system + app files)
#   2. New/malicious processes
#   3. Suspicious network connections
#   4. Cron job changes
#   5. SSH authorized_keys changes
#   6. User account changes (/etc/passwd, /etc/shadow)
#   7. Sudoers changes
#   8. Failed SSH brute-force attempts
#   9. Rootkit indicators
#  10. New SUID/SGID binaries
#
# Runs as a systemd service. Logs to /var/log/halavoice-security.log
# Alerts via log (can be extended to email/webhook).
# ============================================================
set -uo pipefail

LOG="/var/log/halavoice-security.log"
STATE_DIR="/var/run/halavoice-security"
SCAN_INTERVAL=300  # 5 minutes between full scans
ALERT_COOLDOWN=600  # 10 minutes between repeat alerts for same issue

# Colors (disabled when not a tty)
if [ -t 1 ]; then
  RED=$'\033[1;31m'; YEL=$'\033[1;33m'; GRN=$'\033[1;32m'; CYN=$'\033[1;36m'; NC=$'\033[0m'
else
  RED=''; YEL=''; GRN=''; CYN=''; NC=''
fi

mkdir -p "$STATE_DIR" "$(dirname "$LOG")" 2>/dev/null

log() {
  local ts
  ts=$(date '+%Y-%m-%d %H:%M:%S')
  echo "[$ts] $*" >> "$LOG"
}

alert() {
  local level="$1"; shift
  log "[$level] $*"
  echo "${RED}[ALERT][$level]${NC} $*" 2>/dev/null
}

info() {
  log "[INFO] $*"
}

# Prevent duplicate alerts within cooldown period
should_alert() {
  local key="$1"
  local state_file="$STATE_DIR/alert_$(echo "$key" | md5sum | awk '{print $1}')"
  if [ -f "$state_file" ]; then
    local last_alert
    last_alert=$(cat "$state_file" 2>/dev/null || echo 0)
    local now
    now=$(date +%s)
    if (( now - last_alert < ALERT_COOLDOWN )); then
      return 1
    fi
  fi
  date +%s > "$state_file"
  return 0
}

# Take a baseline snapshot of critical files
take_baseline() {
  info "Taking baseline snapshot..."
  # System files
  find /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/sudoers /etc/ssh/sshd_config \
    -type f 2>/dev/null | xargs md5sum 2>/dev/null > "$STATE_DIR/baseline-system.md5"

  # SSH keys
  find /root/.ssh /home/*/.ssh -name "authorized_keys" -type f 2>/dev/null \
    | xargs md5sum 2>/dev/null > "$STATE_DIR/baseline-ssh.md5"

  # Cron files
  find /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly \
    /var/spool/cron -type f 2>/dev/null | xargs md5sum 2>/dev/null > "$STATE_DIR/baseline-cron.md5"

  # Sudoers
  find /etc/sudoers.d -type f 2>/dev/null | xargs md5sum 2>/dev/null > "$STATE_DIR/baseline-sudoers.md5"

  # App .env files
  find /home/ashraffarid2010 -name ".env" -type f 2>/dev/null \
    | xargs md5sum 2>/dev/null > "$STATE_DIR/baseline-env.md5"

  # Record user list
  awk -F: '{print $1":"$3":"$7}' /etc/passwd > "$STATE_DIR/baseline-users.txt"

  # Record running processes
  ps auxww --no-headers > "$STATE_DIR/baseline-processes.txt"

  # Record listening ports
  ss -tlnp 2>/dev/null > "$STATE_DIR/baseline-ports.txt"

  info "Baseline snapshot complete"
}

# Check file integrity against baseline
check_file_integrity() {
  local label="$1"
  local baseline="$STATE_DIR/baseline-${label}.md5"
  [ -f "$baseline" ] || return 0

  local current
  current=$(cat "$baseline" 2>/dev/null | awk '{print $1}' | sort)
  local saved
  saved=$(awk '{print $1}' "$baseline" 2>/dev/null | sort)

  if [ "$current" != "$saved" ]; then
    # Find which files changed
    while IFS= read -r line; do
      local hash file
      hash=$(echo "$line" | awk '{print $1}')
      file=$(echo "$line" | awk '{print $2}')
      local orig_hash
      orig_hash=$(grep "$file" "$baseline" 2>/dev/null | awk '{print $1}')
      if [ "$hash" != "$orig_hash" ] && [ -n "$orig_hash" ]; then
        if should_alert "integrity-${label}-${file}"; then
          alert "HIGH" "File modified: $label:$file (was $orig_hash, now $hash)"
        fi
      fi
    done < <(md5sum $baseline 2>/dev/null; md5sum "$baseline" 2>/dev/null)
    # Simpler approach: just report the baseline changed
    if should_alert "integrity-${label}-changed"; then
      alert "HIGH" "Baseline integrity changed for: $label — files may have been modified"
    fi
  fi
}

# Monitor for new suspicious processes
check_processes() {
  local current_procs
  current_procs=$(ps auxww --no-headers 2>/dev/null)

  # Check for processes running from temp dirs
  local bad_procs
  bad_procs=$(echo "$current_procs" | grep -E '/tmp/|/dev/shm/|/var/tmp/' || true)
  if [ -n "$bad_procs" ]; then
    if should_alert "proc-temp"; then
      alert "CRITICAL" "Process executing from temp directory: $bad_procs"
    fi
  fi

  # Check for processes with deleted binaries
  local del_procs
  del_procs=$(ls -l /proc/*/exe 2>/dev/null | grep '(deleted)' || true)
  if [ -n "$del_procs" ]; then
    if should_alert "proc-deleted"; then
      alert "MEDIUM" "Process with deleted binary: $(echo "$del_procs" | head -3)"
    fi
  fi

  # Check for known malware process names
  local malware_procs
  malware_procs=$(echo "$current_procs" | grep -iE 'xmrig|minerd|kinsing|kdevtmpfsi|cavsystem|cryptonight' || true)
  if [ -n "$malware_procs" ]; then
    if should_alert "proc-malware"; then
      alert "CRITICAL" "Known malware process detected: $malware_procs"
    fi
  fi

  # Check for new root-level processes not in baseline
  local new_root_procs
  new_root_procs=$(echo "$current_procs" | awk '$1=="root" {print $NF}' | sort -u)
  local baseline_root_procs
  baseline_root_procs=$(awk '$1=="root" {print $NF}' "$STATE_DIR/baseline-processes.txt" 2>/dev/null | sort -u)
  local truly_new
  truly_new=$(comm -13 <(echo "$baseline_root_procs") <(echo "$new_root_procs") || true)
  if [ -n "$truly_new" ]; then
    if should_alert "proc-new-root"; then
      alert "MEDIUM" "New root processes: $truly_new"
    fi
  fi
}

# Monitor network connections
check_network() {
  # Check for reverse shells
  local rev_shells
  rev_shells=$(ss -tnp 2>/dev/null | grep -E 'ESTAB.*:(bash|sh|nc|ncat|socat|python|perl|ruby|php)' || true)
  if [ -n "$rev_shells" ]; then
    if should_alert "net-revshell"; then
      alert "CRITICAL" "Possible reverse shell: $rev_shells"
    fi
  fi

  # Check for connections to known malicious IPs/domains
  local mal_ips
  mal_ips=$(ss -tnp 2>/dev/null | grep -E 'cavsystem|51\.39\.231\.144' || true)
  if [ -n "$mal_ips" ]; then
    if should_alert "net-malicious"; then
      alert "CRITICAL" "Connection to known malicious endpoint: $mal_ips"
    fi
  fi

  # Check for new listening ports
  local current_ports
  current_ports=$(ss -tlnp 2>/dev/null | awk 'NR>1{print $4}' | sort)
  local baseline_ports
  baseline_ports=$(awk 'NR>1{print $4}' "$STATE_DIR/baseline-ports.txt" 2>/dev/null | sort)
  local new_ports
  new_ports=$(comm -13 <(echo "$baseline_ports") <(echo "$current_ports") || true)
  if [ -n "$new_ports" ]; then
    if should_alert "net-newport"; then
      alert "MEDIUM" "New listening port(s): $(echo "$new_ports" | tr '\n' ' ')"
    fi
  fi
}

# Monitor cron changes
check_cron() {
  local current
  current=$(find /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly \
    /var/spool/cron -type f 2>/dev/null | xargs md5sum 2>/dev/null | sort)
  local baseline
  baseline=$(sort "$STATE_DIR/baseline-cron.md5" 2>/dev/null)

  if [ "$current" != "$baseline" ]; then
    if should_alert "cron-changed"; then
      alert "HIGH" "Cron job(s) modified — check for persistence"
    fi
  fi
}

# Monitor SSH key changes
check_ssh() {
  local current
  current=$(find /root/.ssh /home/*/.ssh -name "authorized_keys" -type f 2>/dev/null \
    | xargs md5sum 2>/dev/null | sort)
  local baseline
  baseline=$(sort "$STATE_DIR/baseline-ssh.md5" 2>/dev/null)

  if [ "$current" != "$baseline" ]; then
    if should_alert "ssh-changed"; then
      alert "CRITICAL" "SSH authorized_keys modified — possible backdoor"
      diff <(echo "$baseline") <(echo "$current") 2>/dev/null | grep '^[<>]' | head -10 >> "$LOG"
    fi
  fi
}

# Monitor user account changes
check_accounts() {
  local current
  current=$(awk -F: '{print $1":"$3":"$7}' /etc/passwd | sort)
  local baseline
  baseline=$(sort "$STATE_DIR/baseline-users.txt" 2>/dev/null)

  if [ "$current" != "$baseline" ]; then
    if should_alert "accounts-changed"; then
      alert "HIGH" "User account(s) modified"
      diff <(echo "$baseline") <(echo "$current") 2>/dev/null | grep '^[<>]' | head -10 >> "$LOG"
    fi
  fi

  # Check for new UID 0 accounts
  local uid0
  uid0=$(awk -F: '($3==0 && $1!="root"){print $1}' /etc/passwd 2>/dev/null)
  if [ -n "$uid0" ]; then
    if should_alert "uid0-${uid0}"; then
      alert "CRITICAL" "Non-root UID 0 account detected: $uid0"
    fi
  fi
}

# Monitor sudoers changes
check_sudoers() {
  local current
  current=$(find /etc/sudoers.d -type f 2>/dev/null | xargs md5sum 2>/dev/null | sort)
  local baseline
  baseline=$(sort "$STATE_DIR/baseline-sudoers.md5" 2>/dev/null)

  if [ "$current" != "$baseline" ]; then
    if should_alert "sudoers-changed"; then
      alert "HIGH" "Sudoers file(s) modified — check for privilege escalation"
      diff <(echo "$baseline") <(echo "$current") 2>/dev/null | grep '^[<>]' | head -10 >> "$LOG"
    fi
  fi
}

# Monitor brute-force SSH attempts
check_bruteforce() {
  local recent_fails
  recent_fails=$(lastb 2>/dev/null | awk '{print $3}' | sort | uniq -c | sort -rn | head -5)
  if [ -n "$recent_fails" ]; then
    # Check if any IP has >50 failed attempts since last check
    while IFS= read -r line; do
      local count ip
      count=$(echo "$line" | awk '{print $1}')
      ip=$(echo "$line" | awk '{print $2}')
      if [ -n "$ip" ] && [ "$count" -gt 50 ]; then
        if should_alert "bruteforce-${ip}"; then
          alert "HIGH" "SSH brute-force: $count failed attempts from $ip"
        fi
      fi
    done <<< "$recent_fails"
  fi
}

# Monitor for new SUID/SGID binaries
check_suid() {
  local current
  current=$(find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null \
    | grep -v -E '(node_modules|\.cache|/proc/|/sys/)' | sort)
  local baseline="$STATE_DIR/baseline-suid.txt"

  if [ -f "$baseline" ]; then
    local new_suid
    new_suid=$(comm -13 "$baseline" <(echo "$current") || true)
    if [ -n "$new_suid" ]; then
      if should_alert "suid-new"; then
        alert "HIGH" "New SUID/SGID binary(ies): $new_suid"
      fi
    fi
  else
    echo "$current" > "$baseline"
  fi
}

# Check for IOCs (indicators of compromise)
check_iocs() {
  local IOC_REGEX='cavsystem\.com|Kermit123|curl[^|]*\|[[:space:]]*(sh|bash)|wget[^|]*\|[[:space:]]*(sh|bash)|/dev/tcp/|base64[[:space:]]+-d[[:space:]]*\|[[:space:]]*(sh|bash)|chpasswd|\.onion|xmrig|minerd|kinsing|kdevtmpfsi'

  # Check critical locations
  for f in /etc/crontab /etc/cron.d/* /var/spool/cron/root /etc/sudoers.d/*; do
    [ -f "$f" ] || continue
    if grep -qE "$IOC_REGEX" "$f" 2>/dev/null | grep -vE '^\s*#' >/dev/null 2>&1; then
      if should_alert "ioc-$(basename "$f")"; then
        alert "CRITICAL" "IOC pattern found in $f"
        grep -nE "$IOC_REGEX" "$f" 2>/dev/null | grep -vE '^\s*[0-9]+:\s*#' >> "$LOG"
      fi
    fi
  done

  # Check shell init files
  for f in /etc/profile /etc/bash.bashrc /root/.bashrc /root/.bash_profile; do
    [ -f "$f" ] || continue
    if grep -qE "$IOC_REGEX" "$f" 2>/dev/null; then
      if should_alert "ioc-shell-$(basename "$f")"; then
        alert "CRITICAL" "IOC pattern in shell init: $f"
      fi
    fi
  done

  # Check for LD_PRELOAD
  if [ -s /etc/ld.so.preload ]; then
    if should_alert "ioc-preload"; then
      alert "CRITICAL" "Non-empty /etc/ld.so.preload (possible rootkit)"
    fi
  fi
}

# Full security scan (runs less frequently)
full_scan() {
  info "Starting full security scan..."
  check_file_integrity "system"
  check_file_integrity "ssh"
  check_file_integrity "cron"
  check_file_integrity "sudoers"
  check_file_integrity "env"
  check_accounts
  check_sudoers
  check_suid
  check_iocs
  info "Full scan complete"
}

# Quick scan (runs every cycle)
quick_scan() {
  check_processes
  check_network
  check_cron
  check_ssh
  check_bruteforce
}

# Signal handling for clean shutdown
cleanup() {
  info "Security daemon shutting down (PID $$)"
  exit 0
}
trap cleanup SIGTERM SIGINT SIGHUP

# ============================================================
# MAIN
# ============================================================
info "=== HalaVoice Security Daemon started (PID $$) ==="
info "Scan interval: ${SCAN_INTERVAL}s, Alert cooldown: ${ALERT_COOLDOWN}s"

# Take initial baseline
take_baseline

# Update baseline of SUID files
find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null \
  | grep -v -E '(node_modules|\.cache|/proc/|/sys/)' | sort > "$STATE_DIR/baseline-suid.txt" 2>/dev/null

LAST_FULL_SCAN=0

# Main loop
while true; do
  NOW=$(date +%s)

  # Quick scan every cycle
  quick_scan

  # Full scan every SCAN_INTERVAL
  if (( NOW - LAST_FULL_SCAN >= SCAN_INTERVAL )); then
    full_scan
    LAST_FULL_SCAN=$NOW
    # Refresh baseline periodically (in case changes are legitimate)
    take_baseline 2>/dev/null
  fi

  sleep "$SCAN_INTERVAL"
done
