#!/bin/bash
set -euo pipefail
shopt -s inherit_errexit

# shellcheck source=scripts/libcc
. /usr/lib/common-criteria/scripts/libcc

###########################################
# Hardware crypto acceleration disabling (x86_64 OpenSSL bypass + SHA-NI
# CPUID mask; kernel module blacklist is in config/cc-modules-blacklist.conf)

# OpenSSL's own CPUID-based dispatch is independent of the kernel-level
# clearcpuid= mechanism below, so this is required in addition to it, not
# instead of it (guide's "OpenSSL on x86 Architecture" section).
ENV_DROPIN_DIR="/etc/environment.d"
ENV_DROPIN="$ENV_DROPIN_DIR/50-cc-crypto.conf"
GRUB_DEFAULT="/etc/default/grub"

# SHA-NI is bundled inside sha1_ssse3/sha256_ssse3 on this kernel rather than
# being its own module, so unlike the other modules in
# config/cc-modules-blacklist.conf, it can't be disabled via the modprobe
# blacklist without also losing the AVX2/AVX/SSSE3 codepaths - masking the
# CPUID bit at boot instead leaves those intact (guide's "x86 Configuration"
# and "OpenSSL on x86 Architecture" sections). GRUB_CMDLINE_LINUX_DEFAULT only
# takes effect on the *next* boot, so this check distinguishes "the config
# files are correct" (checked first) from "clearcpuid is active in the
# currently running kernel" (the /proc/cmdline check last) - returning 2, not
# 1, when only the latter is missing, since nothing is actually wrong, a
# reboot just hasn't happened yet (see scripts/libcc's cc_exit).
check_50_config_crypto_hwaccel() {
	local arch
	arch=$(uname -m)
	case "$arch" in
	x86_64)
		if [ ! -f "$ENV_DROPIN" ]; then
			echo "$ENV_DROPIN missing"
			return 1
		fi
		if ! grep -q 'OPENSSL_ia32cap=~0x200000200000000' "$ENV_DROPIN"; then
			echo "$ENV_DROPIN missing the expected OPENSSL_ia32cap override"
			return 1
		fi
		if [ ! -f "$GRUB_DEFAULT" ]; then
			echo "$GRUB_DEFAULT missing"
			return 1
		fi
		if ! grep -q "clearcpuid=sha_ni" "$GRUB_DEFAULT"; then
			echo "$GRUB_DEFAULT missing clearcpuid=sha_ni"
			return 1
		fi
		if ! cc_grub_cfg_has "clearcpuid=sha_ni"; then
			echo "not every grub.cfg has clearcpuid=sha_ni (grub2-mkconfig not yet regenerated with it, or no grub.cfg found at all)"
			return 1
		fi
		if ! grep -q "clearcpuid=sha_ni" /proc/cmdline; then
			echo "configured correctly, but the running kernel's cmdline does not have clearcpuid=sha_ni yet - reboot required"
			return 2
		fi
		;;
	*)
		echo "hardware crypto acceleration disabling not implemented for $arch"
		return 1
		;;
	esac
}

apply_50_config_crypto_hwaccel() {
	local arch
	local rc
	arch=$(uname -m)

	case "$arch" in
	x86_64)
		rc=0
		check_50_config_crypto_hwaccel >/dev/null 2>&1 || rc=$?
		if [ "$rc" -eq 0 ]; then
			cc_echo "hardware crypto acceleration already disabled"
			return 0
		fi
		if [ "$rc" -eq 2 ]; then
			cc_echo "hardware crypto acceleration already configured, reboot required before it's fully in effect"
			return 2
		fi

		mkdir -p "$ENV_DROPIN_DIR"
		cat > "$ENV_DROPIN" <<EOF
# Common Criteria evaluated configuration - managed by certification-sles-eal4.
# Do not edit; changes here will be overwritten.
OPENSSL_ia32cap=~0x200000200000000
EOF
		chmod 644 "$ENV_DROPIN"
		cc_echo "Installed $ENV_DROPIN"

		if [ ! -f "$GRUB_DEFAULT" ] || ! command -v grub2-mkconfig >/dev/null 2>&1; then
			cc_echo "FAILED: grub2 not found ($GRUB_DEFAULT or grub2-mkconfig missing) - cannot configure clearcpuid=sha_ni"
			return 1
		fi

		if grep -q "clearcpuid=sha_ni" "$GRUB_DEFAULT"; then
			cc_echo "clearcpuid=sha_ni already present in $GRUB_DEFAULT"
		else
			sed '/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/"$/ clearcpuid=sha_ni"/' "$GRUB_DEFAULT" > "$GRUB_DEFAULT.$$"
			cc_replace "$GRUB_DEFAULT.$$" "$GRUB_DEFAULT"
			rm -f "$GRUB_DEFAULT.$$"
			cc_grub_regenerate
			cc_echo "clearcpuid=sha_ni added to $GRUB_DEFAULT and every applicable grub.cfg regenerated"
		fi

		rc=0
		check_50_config_crypto_hwaccel >/dev/null 2>&1 || rc=$?
		if [ "$rc" -eq 2 ]; then
			cc_echo "clearcpuid=sha_ni configured - reboot required before it's fully in effect"
			return 2
		fi
		if [ "$rc" -ne 0 ]; then
			cc_echo "FAILED: clearcpuid=sha_ni not present in every grub.cfg after regeneration"
			return 1
		fi
		;;
	aarch64)
		cc_echo "ARM64 hardware crypto acceleration disabling: not yet implemented"
		return 1
		;;
	s390x)
		cc_echo "IBM Z hardware crypto acceleration disabling: not yet implemented"
		return 1
		;;
	*)
		cc_echo "Unrecognized architecture $arch: not yet implemented"
		return 1
		;;
	esac
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
	cc_start_logging
	trap 'cc_exit $?' ERR
	apply_50_config_crypto_hwaccel
	cc_exit 0
fi
