#!/bin/bash
# SPDX-License-Identifier: Apache-2.0
# Copyright 2021 Authors of KubeArmor

realpath() {
    CURR=$PWD

    cd "$(dirname "$0")"
    LINK=$(readlink "$(basename "$0")")

    while [ "$LINK" ]; do
        cd "$(dirname "$LINK")"
        LINK=$(readlink "$(basename "$1")")
    done

    REALPATH="$PWD/$(basename "$1")"
    echo "$REALPATH"
}

MOD_DIR=`dirname $(realpath "$0")`
cd $MOD_DIR

generate_requirements() {
    semanage fcontext -l | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo >> karmor.te
    echo "require {" >> karmor.te

    while read line; do
        echo "    type $line;" >> karmor.te
    done < karmor.tmp

    echo "}" >> karmor.te
}

generate_unconfined_service_rules() {
    semanage fcontext -l | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo >> karmor.te

    echo "require {" >> karmor.te
    echo "    type unconfined_service_t;" >> karmor.te
    echo "}" >> karmor.te

    echo >> karmor.te

    echo "allow unconfined_service_t unconfined_service_t:capability2 { mac_admin };" >> karmor.te

    echo >> karmor.te

    while read line; do
        echo "allow unconfined_service_t $line:file { getattr relabelfrom relabelto };" >> karmor.te
    done < karmor.tmp
}

generate_karmor_base() {
    # directory and files

    semanage fcontext -l | grep "all files" | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo > karmor.base

    while read line; do
        echo "allow_dir_access(karmor_t, $line)" >> karmor.base
        echo "allow_file_access(karmor_t, $line)" >> karmor.base
    done < karmor.tmp

    # directory

    semanage fcontext -l | grep "directory" | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo >> karmor.base

    while read line; do
        echo "allow_dir_access(karmor_t, $line)" >> karmor.base
        echo "allow_file_access(karmor_t, $line)" >> karmor.base
    done < karmor.tmp

    # files

    semanage fcontext -l | grep -v "all files" | grep -v "directory" | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo >> karmor.base

    while read line; do
        echo "allow_file_access(karmor_t, $line)" >> karmor.base
    done < karmor.tmp
}

generate_karmor_directory_base() {
    # directory and files

    semanage fcontext -l | grep "all files" | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo > karmor.dir

    while read line; do
        echo "allow_dir_access(karmor_t, $line)" >> karmor.dir
    done < karmor.tmp

    # directory

    semanage fcontext -l | grep "directory" | awk -F"object_r:" '{print $2}' | awk 'NF' | sort -u | awk -F':' '{print $1}' > karmor.tmp

    echo >> karmor.dir

    while read line; do
        echo "allow_dir_access(karmor_t, $line)" >> karmor.dir
    done < karmor.tmp
}

generate_karmor() {
    echo "# automatically generated (do not edit this file)" > karmor.te
    echo >> karmor.te
    echo "policy_module(karmor, 1.0.0)" >> karmor.te
    echo >> karmor.te

    echo "########################################" >> karmor.te

    cat default/common_macro >> karmor.te

    echo >> karmor.te
    echo "########################################" >> karmor.te

    cat default/common_template >> karmor.te

    echo >> karmor.te
    echo "########################################" >> karmor.te

    cat default/karmor_macro >> karmor.te

    echo >> karmor.te
    echo "########################################" >> karmor.te

    # generate required types
    generate_requirements

    # generate rules for unconfined_service_t
    generate_unconfined_service_rules

    # generate base file and directory rules
    generate_karmor_base

    # generate base directory rules
    generate_karmor_directory_base

    echo >> karmor.te
    echo "########################################" >> karmor.te

    cat default/karmor_global >> karmor.te

    echo >> karmor.tmp
    echo "########################################" >> karmor.tmp

    ## default posture ##

    for elem in {0..2} # 3 elements (block, audit, allow)
    do
        defaultPosture="block"

        if [[ $elem == 1 ]]; then
            defaultPosture="audit"
        elif [[ $elem == 2 ]]; then
            defaultPosture="allow"
        fi

        ## fromSource ##

        for code in {0..15} # 4 elements -> 2^4 (matchProtocols)
        do
            cat default/karmor_template > karmor.tmp

            TCP="n"
            UDP="n"
            ICMP="n"
            RAW="n"

            # tcp
            if (( (code & 0x1) == 0x1 )); then
                echo "allow_tcp_network_operation(karmor_t)" >> karmor.tmp
                TCP="t"
            fi

            # udp
            if (( (code & 0x2) == 0x2 )); then
                echo "allow_udp_network_operation(karmor_t)" >> karmor.tmp
                UDP="u"
            fi

            # icmp
            if (( (code & 0x4) == 0x4 )); then
                echo "allow_icmp_network_operation(karmor_t)" >> karmor.tmp
                echo "allow_raw_network_operation(karmor_t)" >> karmor.tmp
                ICMP="i"
            fi

            # raw
            if (( (code & 0x8) == 0x8 )); then
                if (( (code & 0x4) != 0x4 )); then
                    echo "allow_raw_network_operation(karmor_t)" >> karmor.tmp
                fi
                RAW="r"
            fi

            if [[ $defaultPosture != "block" ]]; then
                echo >> karmor.tmp
                echo "########################################" >> karmor.tmp

                cat karmor.base >> karmor.tmp
            else
                echo >> karmor.tmp
                echo "########################################" >> karmor.tmp

                cat karmor.dir >> karmor.tmp
            fi

            echo >> karmor.tmp
            echo "########################################" >> karmor.tmp

            sed "s/karmor\_/karmor\_$defaultPosture\_$TCP$UDP$ICMP$RAW\_/g" karmor.tmp >> karmor.te
        done

        ##

    done

    ##
}

compile_and_inject_selinux_module() {
    # clean up the previous module
    rm -rf karmor.fc karmor.if karmor.pp karmor.base karmor.dir karmor.tmp tmp

    # create files
    echo > karmor.fc
    echo > karmor.if

    # compile and insert selinux module
    make -f /usr/share/selinux/devel/Makefile karmor.pp && semodule -i karmor.pp
    if [ $? != 0 ]; then
        # remove temp files
        rm -rf karmor.fc karmor.if karmor.pp karmor.base karmor.dir karmor.tmp tmp

        echo "Failed to install $1 SELinux module"
        exit 1
    fi

    # remove temp files
    rm -rf karmor.fc karmor.if karmor.pp karmor.base karmor.dir karmor.tmp tmp
}

# step 1
generate_karmor

# step 2
compile_and_inject_selinux_module

exit 0
