#!/bin/sh

#shellcheck disable=SC1091
. /etc/control.d/functions

OPENSSL_CNF="${OPENSSL_CNF:-/etc/openssl/openssl.cnf}"
CONFIG="$OPENSSL_CNF"

# Adapt OpenSSL for GOST cryptography support

def_OPENSSL_CONF="openssl_init"
def_ENGINES="engine_section"
def_PROVIDERS="provider_sect"
def_GOST="gost_section"
def_DEFPROV="default_sect"
def_GOSTPROV="gost_prov_sect"

DEFAULTS="
engine_id           gost
default_algorithms  ALL
CRYPT_PARAMS        id-Gost28147-89-CryptoPro-A-ParamSet
"

main_section_get() {
    sed -n -e "1,/^\\[/ {
        /^\\[/ d;
        /^[[:space:]]*$2[[:space:]]*=/ {
            s/^[[:space:]]*$2[[:space:]]*=[[:space:]]*//;
            s/[[:space:]]*\$//;
            p; q
        }
    }" "$1"
}

main_section_set() {
    sed -i \
        -e "/^$2[[:space:]]*=[[:space:]]*$3\\([[:space:]].*\\)\\?\$/ b exit" \
        -e "/^$2[[:space:]]*=/ { s/^$2[[:space:]]*=.*\$/$2 = $3/; b exit }"  \
        -e "/^[[:space:]]*\\[[[:space:]]*[^]]\\+[[:space:]]*\\]\\([[:space:]]*#.*\\)\\?\$/ {
                s/^.*\$/$2 = $3\n\n&/;
                b exit;
            }" \
        -e '$ q1; p; d' \
        -e ':exit $ q0; n; b exit' \
        "$1"
}

osslcnf_get() {
    sed -n -e "/^[[:space:]]*\\[[[:space:]]*$2[[:space:]]*\\]/! { $ q2; d }" \
           -e ':loop n' \
           -e "/^[[:space:]]*\\[/! {
                 /^[[:space:]]*$3[[:space:]]=/ {
                   s/^[[:space:]]*$3[[:space:]]=[[:space:]]*//;
                   s/#.*\$//;
                   p; q0
                 }
               }" \
          -e '$ q1; b loop' \
        "$1"
}

_osslcnf_set_nonempty() {
    sed -i -e "/^[[:space:]]*\\[[[:space:]]*$2[[:space:]]*\\]/! {
                 \$ {
                   s/^.*\$/&\n\n[ $2 ]\n$3 = $4/;
                   b exit;
                 };
                 p; d
               }" \
           -e "\$ {
                 s/^.*\$/&\n$3 = $4/;
                 q0
               }" \
           -e ':loop n' \
           -e "/^[[:space:]]*\\[/! {
                 /^[[:space:]]*$3[[:space:]]=/ {
                   s/^\\([[:space:]]*$3[[:space:]]=[[:space:]]*\\).*\$/\\1$4/;
                   b exit
                 };
                 \$ {
                   s/^.*\$/&\n$3 = $4/;
                   q0
                 };
                 /^[[:space:]]*\$/ {
                   s/^.*\$/$3 = $4\n&/;
                   b exit
                 };
                 b loop
               }" \
           -e "/^[[:space:]]*\\[/ {
                 s/^.*\$/$3 = $4\n\n&/;
                 b exit
               }" \
           -e 'b loop' \
           -e ':exit $ q0; n; b exit' \
           "$1"
}

osslcnf_del() {
    sed -n -i \
           -e ":sec /^[[:space:]]*\\[[[:space:]]*$2[[:space:]]*\\]/! {
                 \$ { p; q0; }; p; d
               }" \
           -e ':print p' \
           -e ':skip n' \
           -e "/^[[:space:]]*\\[/! {
                 /^[[:space:]]*$3[[:space:]]=/ b skip;
               }" \
           -e '/^[[:space:]]*\[/ b sec' \
           -e 'b print' \
           -e ':exit $ { p; q0 }; p; n; b exit' \
           "$1"
}

osslcnf_set() {
    if [ -n "${4:-}" ]; then
        _osslcnf_set_nonempty "$@"
    else
        osslcnf_del "$1" "$2" "$3"
    fi
}

_tmp_config=
_copy_mode() {
    _tmp_config="$(mktemp --tmpdir openssl-gost.XXXX)"
    chown --reference="$CONFIG" "$_tmp_config"
    chmod --reference="$CONFIG" "$_tmp_config"
}

_apply_mode() {
    chown --reference="$_tmp_config" "$CONFIG"
    chmod --reference="$_tmp_config" "$CONFIG"
    rm "$_tmp_config"
}

_backup() {
    rm -f "${CONFIG}~"
    cp -a "$CONFIG" "${CONFIG}~"
}

OPENSSL_CONF=
ENGINES=
PROVIDERS=
GOST=
DEFPROV=
GOSTPROV=
read_sections() {
    OPENSSL_CONF=
    ENGINES=
    GOST=

    OPENSSL_CONF="$(main_section_get "$CONFIG" 'openssl_conf')"
    if [ -n "$OPENSSL_CONF" ]; then
        ENGINES="$(osslcnf_get "$CONFIG" "$OPENSSL_CONF" 'engines')"
        if [ -n "$ENGINES" ]; then
            GOST="$(osslcnf_get "$CONFIG" "$ENGINES" 'gost')"
        fi
        PROVIDERS="$(osslcnf_get "$CONFIG" "$OPENSSL_CONF" 'providers')"
        if [ -n "$PROVIDERS" ]; then
            GOSTPROV="$(osslcnf_get "$CONFIG" "$PROVIDERS" 'gostprov')"
	    DEFPROV="$(osslcnf_get "$CONFIG" "$PROVIDERS" 'default')"
        fi
    fi
}

enable_gost_engine() {
    _copy_mode
    _backup
    
    main_section_set "$CONFIG" 'openssl_conf' \
                     "${OPENSSL_CONF:-$def_OPENSSL_CONF}"
    osslcnf_set "$CONFIG" "${OPENSSL_CONF:-$def_OPENSSL_CONF}" \
                'engines' "${ENGINES:-$def_ENGINES}"
    osslcnf_set "$CONFIG" "${ENGINES:-$def_ENGINES}" 'gost' \
                "${GOST:-$def_GOST}"

    echo "$DEFAULTS" | while read -r key val; do
        [ -n "$key" ] || continue
        osslcnf_set "$CONFIG" "${GOST:-$def_GOST}" "$key" "$val"
    done

    _apply_mode
}

enable_gost_provider() {
    _copy_mode
    _backup

    main_section_set "$CONFIG" 'openssl_conf' \
                     "${OPENSSL_CONF:-$def_OPENSSL_CONF}"
    osslcnf_set "$CONFIG" "${OPENSSL_CONF:-$def_OPENSSL_CONF}" \
                'providers' "${PROVIDERS:-$def_PROVIDERS}"
    osslcnf_set "$CONFIG" "${PROVIDERS:-$def_PROVIDERS}" 'gostprov' \
                "${GOSTPROV:-$def_GOSTPROV}"
    osslcnf_set "$CONFIG" "${GOSTPROV:-$def_GOSTPROV}" 'activate' 1
    osslcnf_set "$CONFIG" "${DEFPROV:-$def_DEFPROV}" 'activate' 1

    _apply_mode
}

disable_gost_engine() {
    [ -n "$ENGINES" ] || return 0

    _copy_mode
    _backup

    osslcnf_del "$CONFIG" "$ENGINES" 'gost'

    _apply_mode
}

disable_gost_provider() {
    [ -n "$GOSTPROV" ] || return 0

    _copy_mode
    _backup

    osslcnf_set "$CONFIG" "${GOSTPROV:-$def_GOSTPROV}" 'activate' 0

    _apply_mode
}

is_gost_engine_enabled() {
    [ -n "$GOST" ] || return 1

    echo "$DEFAULTS" | while read -r key dval; do
        [ -n "$key" ] || continue
        # Currently, check only the 'engine_id' parameter.
        case "$key" in
            engine_id)
                ;;
            *)
                continue
                ;;
        esac
        val="$(osslcnf_get "$CONFIG" "$GOST" "$key")"
        if [ "$val" != "$dval" ]; then
            exit 1  # exit from the subshell
        fi
    done
}

is_gost_provider_enabled() {
    [ -n "$GOSTPROV" ] || return 1

    case "$(osslcnf_get "$CONFIG" "$GOSTPROV" 'activate' | tr '[:upper:]' '[:lower:]')" in
	yes|on|true|1)
	    return 0
	    ;;
    esac

    return 1
}


## Main

read_sections

new_summary "Enable/disable GOST ciphers with OpenSSL"
new_help 'enabled' "Enable GOST engine"
new_help 'disabled' "Disable GOST engine and provider"
new_help 'all' "Enable GOST engine and provider"
new_help 'provider_only' "Enable GOST provider only"

REQUEST="$*"

case "$REQUEST" in
	help|'help '*)
		control_help "${REQUEST#help}"
		;;
	list)
		control_list
		;;
	summary)
		control_summary
		;;
	status)
            if is_gost_engine_enabled && is_gost_provider_enabled; then
		echo 'all'
	    elif is_gost_engine_enabled; then
		echo 'enabled'
	    elif is_gost_provider_enabled; then
		echo 'provider_only'
            else
		echo 'disabled'
            fi
		;;
	enabled)
	    disable_gost_provider
            enable_gost_engine
		;;
	all)
            enable_gost_engine
            enable_gost_provider
		;;
	provider_only)
            disable_gost_engine
            enable_gost_provider
		;;
	disabled)
            disable_gost_engine
            disable_gost_provider
		;;
    *)
		printf '%s: %s\n' "${0##*/}" "Invalid mode: $REQUEST" >&2
		exit 1
		;;
esac
