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

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

###########################################
# FIPS mode (guide: "In the evaluated configuration FIPS mode MUST be
# enabled"). On x86_64, fips-mode-setup --enable adds fips=1 to the
# bootloader's kernel command line via the same GRUB_CMDLINE_LINUX_DEFAULT
# mechanism as 50-config-crypto-hwaccel's clearcpuid=sha_ni - that only
# takes effect on the *next* boot, so this has the identical "configured
# now, active only after a reboot" shape, checked the same way: config
# files first, then the running kernel's own /proc/cmdline and
# /proc/sys/crypto/fips_enabled as the final, most-authoritative signal.
# IBM Z has no GRUB at all (zipl instead) and isn't handled here yet - same
# "not yet implemented" state as 50-config-crypto-hwaccel's own aarch64/
# s390x branches, rather than assuming a bootloader mechanism that doesn't
# apply there.

GRUB_DEFAULT="/etc/default/grub"

check_52_config_fips() {
	local arch
	arch=$(uname -m)
	case "$arch" in
	x86_64)
		if [ ! -f "$GRUB_DEFAULT" ]; then
			echo "$GRUB_DEFAULT missing"
			return 1
		fi
		if ! grep -q "fips=1" "$GRUB_DEFAULT"; then
			echo "$GRUB_DEFAULT missing fips=1"
			return 1
		fi
		if ! cc_grub_cfg_has "fips=1"; then
			echo "not every grub.cfg has fips=1 (grub2-mkconfig not yet regenerated with it, or no grub.cfg found at all)"
			return 1
		fi
		if ! grep -q "fips=1" /proc/cmdline; then
			echo "configured correctly, but the running kernel's cmdline does not have fips=1 yet - reboot required"
			return 2
		fi
		if [ "$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)" != "1" ]; then
			echo "fips=1 is on the running kernel's cmdline, but /proc/sys/crypto/fips_enabled is not 1 - FIPS self-test may have failed"
			return 1
		fi
		;;
	*)
		echo "FIPS mode enablement not implemented for $arch"
		return 1
		;;
	esac
}

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

	case "$arch" in
	x86_64)
		if ! command -v fips-mode-setup >/dev/null 2>&1; then
			cc_echo "FAILED: fips-mode-setup not found - is the 'fips' pattern/crypto-policies-scripts installed?"
			return 1
		fi

		rc=0
		check_52_config_fips >/dev/null 2>&1 || rc=$?
		if [ "$rc" -eq 0 ]; then
			cc_echo "FIPS mode already active"
			return 0
		fi
		if [ "$rc" -eq 2 ]; then
			cc_echo "FIPS mode already configured, reboot required before it's fully in effect"
			return 2
		fi

		# fips-mode-setup --enable interactively prompts with a 15-second
		# "press control-C to abort" warning unless this is set. It also
		# preserves an already FIPS-based crypto policy rather than
		# resetting it to plain "FIPS" (confirmed in its own source: it
		# only switches to plain FIPS when the current policy is not
		# already FIPS-based), so running this after
		# 51-config-crypto-policies has already set FIPS:OSPP:CC-STRICT
		# is safe regardless of dispatch order.
		FIPS_MODE_SETUP_SKIP_WARNING=1 fips-mode-setup --enable

		rc=0
		check_52_config_fips >/dev/null 2>&1 || rc=$?
		if [ "$rc" -eq 2 ]; then
			cc_echo "fips=1 configured - reboot required before it's fully in effect"
			return 2
		fi
		if [ "$rc" -ne 0 ]; then
			cc_echo "FAILED: FIPS mode not consistent after fips-mode-setup --enable"
			return 1
		fi
		cc_echo "FIPS mode active"
		;;
	*)
		cc_echo "FIPS mode enablement not implemented for $arch"
		return 1
		;;
	esac
}

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