#!/bin/bash
# SPDX-Version: 3.0
# SPDX-CreationInfo: 2025-10-11; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
# SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
# SPDX-PackageName: CISS.debian.live.builder
# SPDX-Security-Contact: security@coresecret.eu
set -Ceuo pipefail

printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ 🧪 '%s' starting ... \e[0m\n" "${0}"

[[ -r /root/ciss_xdg_tmp.sh ]] && . /root/ciss_xdg_tmp.sh
export DEBIAN_FRONTEND="noninteractive" INITRD="No"

apt-get install -y --no-install-recommends kexec-tools busybox-static

install -d -m 0755 /boot/ciss-memwipe
install -d -m 0755 /usr/local/sbin
install -d -m 0755 /etc/systemd/system
install -d -m 0755 /etc/default

### Pick a kernel to kexec into: use the latest installed vmlinuz. -------------------------------------------------------------
# shellcheck disable=SC2012,SC2155
declare _kernel="$(cd /boot && ls -1 vmlinuz-* | sed 's|vmlinuz-||' | sort -V | tail -n1)"
cp -f "/boot/vmlinuz-${_kernel}" /boot/ciss-memwipe/vmlinuz

### Build minimal initramfs with a busybox and a tiny '/init'. -----------------------------------------------------------------
declare TMPDIR; TMPDIR="$(mktemp -d)"
trap 'rm -rf "${TMPDIR}"' EXIT

mkdir -p "${TMPDIR}"/{bin,dev,proc,sys,wipe}
cp -f /bin/busybox.static "${TMPDIR}/bin/busybox"

cat << 'EOF' >| "${TMPDIR}/init"
#!/bin/busybox sh
### Minimal init to wipe RAM, then power off.
### Parses cmdline: ciss_wipe_passes=2 ciss_wipe_mode=zero+random ciss_dd_bs=64M ciss_tmpfs_pct=95
set -eu

get_arg() { # $1=key ; echoes value or empty

  for tok in $(cat /proc/cmdline); do

    case "${tok}" in
      $1=*) echo "${tok#*=}"; return 0;;
    esac

  done

  echo ""
}

mount -t devtmpfs devtmpfs /dev 2>/dev/null || true
[ -e /dev/console ] || mknod -m 600 /dev/console c 5 1
[ -e /dev/null ]    || mknod -m 666 /dev/null c 1 3
[ -e /dev/urandom ] || mknod -m 444 /dev/urandom c 1 9

mount -t proc proc /proc
mount -t sysfs sysfs /sys

PASSES="$(get_arg ciss_wipe_passes)"; [ -n "${PASSES}" ] || PASSES=2
MODE="$(get_arg ciss_wipe_mode)"; [ -n "${MODE}" ] || MODE="zero+random"
BS="$(get_arg ciss_dd_bs)"; [ -n "${BS}" ] || BS=64M
PCT="$(get_arg ciss_tmpfs_pct)"; [ -n "${PCT}" ] || PCT=95

echo 1 > /proc/sys/kernel/printk 2>/dev/null || true

MEM_KB="$(awk '/MemTotal:/ {print $2}' /proc/meminfo)"
SIZE_KB=$(( MEM_KB * PCT / 100 ))
mount -t tmpfs -o "size=${SIZE_KB}k,nodev,nosuid,noexec,mode=0700" tmpfs /wipe

wipe_pass() {
  pattern="$1"
  if [ "${pattern}" = "zero" ]; then
    src="/dev/zero"
  else
    src="/dev/urandom"
  fi

  i=0
  while :; do
    busybox dd if="${src}" of="/wipe/block_${i}" bs="${BS}" status=none || break
    i=$((i+1))
  done
  sync
  echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
  rm -f /wipe/block_* 2>/dev/null || true
  sync
}

DO_ZERO=0; DO_RANDOM=0
case "${MODE}" in
  zero) DO_ZERO=1 ;;
  random) DO_RANDOM=1 ;;
  zero+random|random+zero) DO_ZERO=1; DO_RANDOM=1 ;;
  *) DO_ZERO=1 ;;
esac

p=1
while [ ${p} -le "${PASSES}" ]; do
  [ ${DO_ZERO} -eq 1 ] && wipe_pass zero
  [ ${DO_RANDOM} -eq 1 ] && wipe_pass random
  p=$((p+1))
done

sync
busybox poweroff -f || echo o >| /proc/sysrq-trigger
EOF

chmod +x "${TMPDIR}/init"

( cd "${TMPDIR}" && find . -print0 | cpio --null -ov --format=newc ) | gzip -9 > /boot/ciss-memwipe/initrd.img

cat << 'EOF' >| /etc/default/ciss-memwipe
### CISS Memory Wipe defaults
CISS_WIPE_PASSES=2           # number of passes
CISS_WIPE_MODE="zero+random" # zero | random | zero+random
CISS_WIPE_DD_BS="64M"        # dd block size
CISS_WIPE_TMPFS_PCT=95       # percentage of MemTotal to allocate
EOF

cat << 'EOF' >| /usr/local/sbin/ciss-memwipe
#!/bin/bash
# Prepare and execute kexec-based memory wipe.
set -euo pipefail

. /etc/default/ciss-memwipe || true

KERNEL="/boot/ciss-memwipe/vmlinuz"
INITRD="/boot/ciss-memwipe/initrd.img"

append_common="quiet loglevel=1 ciss_wipe_passes=${CISS_WIPE_PASSES:-2} ciss_wipe_mode=${CISS_WIPE_MODE:-zero+random} ciss_dd_bs=${CISS_WIPE_DD_BS:-64M} ciss_tmpfs_pct=${CISS_WIPE_TMPFS_PCT:-95}"

prepare() {
  # Try to allow kexec if not locked down
  if [ -w /proc/sys/kernel/kexec_load_disabled ] && [ "$(cat /proc/sys/kernel/kexec_load_disabled)" = "1" ]; then
    echo 0 > /proc/sys/kernel/kexec_load_disabled || true
  fi
  # Load wipe kernel
  if command -v kexec >/dev/null 2>&1 && [ -s "${KERNEL}" ] && [ -s "${INITRD}" ]; then
    kexec -l "${KERNEL}" --initrd="${INITRD}" --append="${append_common}" || true
  fi
}

fallback_inplace() {
  # Last-resort: wipe in-place via tmpfs and then power off
  mount -t tmpfs -o "size=95%,nodev,nosuid,noexec,mode=0700" tmpfs /run/wipe 2>/dev/null || mkdir -p /run/wipe
  i=0
  while :; do
    dd if=/dev/zero of="/run/wipe/blk_${i}" bs="${CISS_WIPE_DD_BS:-64M}" status=none || break
    i=$((i+1))
  done
  sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
  rm -f /run/wipe/blk_* 2>/dev/null || true
  sync
  systemctl poweroff -f || poweroff -f || echo o > /proc/sysrq-trigger
}

execute() {
  sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
  # Prefer systemd's path if possible
  if command -v systemctl >/dev/null 2>&1 && systemctl --quiet is-system-running; then
    # If kexec image was loaded, systemctl kexec will use it
    systemctl kexec || kexec -e || fallback_inplace
  else
    kexec -e || fallback_inplace
  fi
}

case "${1:-}" in
  prepare) prepare ;;
  execute) execute ;;
  *) echo "Usage: $0 {prepare|execute}" >&2; exit 2 ;;
esac
EOF
chmod 0755 /usr/local/sbin/ciss-memwipe

### Systemd service: load at boot, execute on shutdown
cat << 'EOF' >| /etc/systemd/system/ciss-memwipe.service
[Unit]
Description=CISS: preload and execute kexec-based RAM wipe on shutdown
DefaultDependencies=no
# Ensure we run late enough on shutdown, but early enough to take over
Before=shutdown.target
After=local-fs.target network.target multi-user.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/ciss-memwipe prepare
# ExecStop runs during shutdown: jump into wipe kernel
ExecStop=/usr/local/sbin/ciss-memwipe execute
TimeoutStartSec=20s
TimeoutStopSec=infinity

[Install]
WantedBy=multi-user.target
EOF

systemctl enable ciss-memwipe.service

printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ ✅ '%s' applied successfully. \e[0m\n" "${0}"

exit 0
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
