#!/bin/sh
### BEGIN INIT INFO
# Provides:            integrity
# Required-Start:      mountfs udev
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start:       3 4 5
# Default-Stop:
# Short-Description:   Enabling IMA/EVM.
# Description:         This service tries to load IMA/EVM keys and
#                      policy and enables the IMA/EVM subsytem.
### END INIT INFO

# A script to configure IMA and EVM enforcement at initrd time.
# Copyright (C) 2019  Mikhail Efremov.
# Copyright (C) 2024  Paul Wolneykien.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

. /etc/init.d/template

IMA_POLICY_ADMIN=/etc/integrity/policy
IMA_POLICY_DEFAULT=/usr/share/integrity/policy
SECFS=/sys/kernel/security

do_start() {
    local need_unmount=
    if ! grep -q  "$SECFS" /proc/mounts; then
	mount -n -t securityfs securityfs "$SECFS" || return 1
	need_unmount=1
    fi

    # search for IMA keyring
    ima_id="$(keyctl search @u keyring _ima)"
    if [ -z "$ima_id" ]; then
	ima_id="$(keyctl newring _ima @u)"
    fi

    [ -n "$ima_id" ] || return 1

    # load GOST modules if needed
    if grep -qsw 'ima_hash=streebog.*' /proc/cmdline; then
	modprobe ecrdsa_generic && \
        modprobe streebog_generic && \
        mkdir -p /var/lib/ssl && \
        cp /etc/openssl/openssl.cnf /var/lib/ssl/ || \
	    return 1
    fi

    # import IMA X509 certificate
    evmctl import /etc/keys/x509_ima.der "$ima_id" || return 1

    if [ \
	 -e /etc/keys/x509_evm.der -a \
	 -e /etc/keys/kmk-user.blob -a \
	 -e /etc/keys/evm-key.blob \
       ]
    then
	# search for EVM keyring
	evm_id="$(keyctl search @u keyring _evm)"
	if [ -z "$evm_id" ]; then
	    evm_id="$(keyctl newring _evm @u)"
	fi

	# import EVM X509 certificate
	evmctl import /etc/keys/x509_evm.der "$evm_id"

	# import EVM encrypted key
	keyctl show | grep -q kmk-user || keyctl add user kmk-user "$(cat /etc/keys/kmk-user.blob)" @u
	keyctl add encrypted evm-key "load $(cat /etc/keys/evm-key.blob)" @u

	# enable EVM
	echo "$(cat /etc/integrity/evm_mode 2>/dev/null || echo '0x80000002')" >"$SECFS"/evm
    fi

    # load policy
    cat "$IMA_POLICY" >"$SECFS"/ima/policy || return 1

    if [ -n "$need_unmount" ]; then
	umount "$SECFS"
    fi
}

start() {
    if [ -f "$IMA_POLICY_ADMIN" ]; then
	IMA_POLICY="$IMA_POLICY_ADMIN"
    elif [ -f "$IMA_POLICY_DEFAULT" ]; then
	IMA_POLICY="$IMA_POLICY_DEFAULT"
    fi

    if grep -qsw 'ima_appraise=enforce' /proc/cmdline &&
       [ -n "$IMA_POLICY" ]
    then
    	echo_msg "Loading IMA policy $IMA_POLICY..."
	if ! do_start 1>/dev/null 2>&1; then
	    echo_failure
	    echo_newline

	    if [ -x /etc/integrity/on-initrd-error ]; then
		/etc/integrity/on-initrd-error
		exit $?
	    elif [ -e /etc/integrity/reboot-on-initrd-error ]; then
		echo "WARNING! Failed to enable IMA. The system will reboot within 30 sec..." >&2
		sleep 30
		echo '6' >/.initrd/telinit
	    fi

	    exit 1
	fi

	echo_success
	echo_newline
    fi
}

switch "${1-}"
