#!/bin/sh
set -eu
LC_ALL=C
export LC_ALL

CONFIG_FILE="/etc/uosecr/uosecr.conf"
CONFIG_DIR="/etc/uosecr"
SYSFS_DIR="/sys/kernel/security/uos_exec_cache_refresh"

CONFIG_KEYS="
mode
fail_closed_root_owned
fail_closed_mmap
sensitive_enable
sensitive_read_enable
fail_closed_sensitive
sensitive_refresh_interval_ms
audit_success
splice_audit
"

log()
{
	echo "uosecr-config: $*" >&2
}

is_uint()
{
	case "$1" in
		""|*[!0-9]*)
			return 1
		;;
	esac

	return 0
}

is_uint32()
{
	value="$(printf '%s' "$1" | sed 's/^0*//')"
	[ -n "$value" ] || value="0"

	case "${#value}" in
		1|2|3|4|5|6|7|8|9)
			return 0
		;;
		10)
			[ "$value" \> "4294967295" ] && return 1
			return 0
		;;
	esac

	return 1
}

is_known_key()
{
	case "$1" in
		mode|fail_closed_root_owned|fail_closed_mmap|sensitive_enable|\
		sensitive_read_enable|fail_closed_sensitive|\
		sensitive_refresh_interval_ms|audit_success|splice_audit)
			return 0
		;;
	esac

	return 1
}

validate_value()
{
	key="$1"
	value="$2"

	if ! is_uint "$value"; then
		log "invalid non-numeric value for $key: $value"
		return 1
	fi

	case "$key" in
		mode)
			case "$value" in
				0|1|2|3)
					return 0
				;;
				*)
					log "invalid mode value: $value"
					return 1
				;;
			esac
		;;
		fail_closed_root_owned|fail_closed_mmap|sensitive_enable|\
		sensitive_read_enable|fail_closed_sensitive|audit_success|\
		splice_audit)
			case "$value" in
				0|1)
					return 0
				;;
				*)
					log "invalid boolean value for $key: $value"
					return 1
				;;
			esac
		;;
		sensitive_refresh_interval_ms)
			if ! is_uint32 "$value"; then
				log "invalid sensitive_refresh_interval_ms value: $value"
				return 1
			fi
		;;
		*)
			log "unknown key: $key"
			return 1
		;;
	esac

	return 0
}

trim_line()
{
	printf '%s' "$1" | sed 's/[[:space:]]*#.*$//;s/^[[:space:]]*//;s/[[:space:]]*$//'
}

load_config()
{
	if [ ! -d "$SYSFS_DIR" ]; then
		log "$SYSFS_DIR is not available; is the uosecr module loaded?"
		return 0
	fi

	if [ ! -f "$CONFIG_FILE" ]; then
		log "$CONFIG_FILE does not exist; keeping current kernel defaults"
		return 0
	fi

	while IFS= read -r raw_line || [ -n "$raw_line" ]; do
		line="$(trim_line "$raw_line")"
		[ -n "$line" ] || continue

		case "$line" in
			*=*)
				key="$(trim_line "${line%%=*}")"
				value="$(trim_line "${line#*=}")"
			;;
			*)
				log "invalid config line: $line"
				return 1
			;;
		esac

		if ! is_known_key "$key"; then
			log "ignoring unknown config key: $key"
			continue
		fi

		validate_value "$key" "$value"

		if [ ! -w "$SYSFS_DIR/$key" ]; then
			log "control $SYSFS_DIR/$key is not writable; skipping"
			continue
		fi

		printf '%s\n' "$value" > "$SYSFS_DIR/$key"
	done < "$CONFIG_FILE"
}

save_config()
{
	if [ ! -d "$SYSFS_DIR" ]; then
		log "$SYSFS_DIR is not available; nothing to save"
		return 0
	fi

	umask 077
	mkdir -p "$CONFIG_DIR"
	tmp_file="$(mktemp "$CONFIG_FILE.tmp.XXXXXX")"

	{
		echo "# uosecr persistent kernel control configuration"
		echo "# This file is updated by uosecr-config.service at shutdown."
		echo
		for key in $CONFIG_KEYS; do
			if [ -r "$SYSFS_DIR/$key" ]; then
				value="$(sed -n '1p' "$SYSFS_DIR/$key")"
				printf '%s=%s\n' "$key" "$value"
			fi
		done
	} > "$tmp_file"

	chmod 0600 "$tmp_file"
	mv "$tmp_file" "$CONFIG_FILE"
}

case "${1:-}" in
	load)
		load_config
	;;
	save)
		save_config
	;;
	*)
		echo "Usage: $0 {load|save}" >&2
		exit 2
	;;
esac
