#!/bin/sh

#################################################################################
#
#   Lynis
# ------------------
#
# Copyright (c) Michael Boelen, CISOfy, and many contributors.
#
# Website  : https://cisofy.com
# Blog     : https://linux-audit.com/
# GitHub   : https://github.com/CISOfy/lynis
#
# Lynis comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
# See LICENSE file for usage of this software.
#
#################################################################################
#
# Firewalls
#
#################################################################################
#
    InsertSection "${SECTION_FIREWALLS}"
#
#################################################################################
#
    IPTABLES_ACTIVE=0
    IP6TABLES_ACTIVE=0
    IPTABLES_INKERNEL_ACTIVE=0
    IPTABLES_MODULE_ACTIVE=0
    FIREWALL_ACTIVE=0
    FIREWALL_EMPTY_RULESET=0
    NFTABLES_ACTIVE=0
#
#################################################################################
#
    # Test        : FIRE-4502
    # Description : Check iptables kernel module
    Register --test-no FIRE-4502 --os Linux --weight L --network NO --category security --description "Check iptables kernel module"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(${LSMODBINARY} | ${AWKBINARY} '{ print $1 }' | ${GREPBINARY} "^ip*_tables")
        if [ -n "${FIND}" ]; then
            FIREWALL_ACTIVE=1
            IPTABLES_ACTIVE=1
            IPTABLES_MODULE_ACTIVE=1
            Display --indent 2 --text "- Checking iptables kernel module" --result "${STATUS_FOUND}" --color GREEN
            Report "firewall_software[]=iptables"
            LogText "Result: Found iptables in loaded kernel modules"
            for I in ${FIND}; do
                if [ "${I}" = "ip6_tables" ]; then IP6TABLES_ACTIVE=1; Report "firewall_software[]=ip6tables"; fi
                LogText "Found module: ${I}"
            done
        elif [ -f ${ROOTDIR}proc/net/ip_tables_names ]; then
            FIREWALL_ACTIVE=1
            Report "firewall_software[]=iptables"
            IPTABLES_ACTIVE=1
            Display --indent 2 --text "- Checking iptables support" --result "${STATUS_FOUND}" --color GREEN
        elif [ -f ${ROOTDIR}proc/net/ip6_tables_names ]; then
            FIREWALL_ACTIVE=1
            IP6TABLES_ACTIVE=1
            Report "firewall_software[]=ip6tables"
            Display --indent 2 --text "- Checking ip6tables support" --result "${STATUS_FOUND}" --color GREEN
        else
            Display --indent 2 --text "- Checking iptables kernel module" --result "${STATUS_NOT_FOUND}" --color WHITE

            # If we can't find an active module, try to find the Linux configuration file and check that
            if [ -f /proc/config.gz ]; then LINUXCONFIGFILE="/proc/config.gz"; tCATCMD="zcat"; fi
            sLINUXCONFIGFILE="/boot/config-$(uname -r)"
            if [ -f ${sLINUXCONFIGFILE} ]; then LINUXCONFIGFILE=${sLINUXCONFIGFILE}; tCATCMD="cat"; fi

            # If we have a kernel configuration file, use it for testing
            # Do not perform test if we already found it in kernel module list, to avoid triggered it in the upcoming
            # tests, when using iptables --list
            if [ -n "${LINUXCONFIGFILE}" ]; then
                if [ -f ${LINUXCONFIGFILE} -a ${IPTABLES_MODULE_ACTIVE} -eq 0 ]; then
                    LogText "Result: found kernel configuration file (${LINUXCONFIGFILE})"
                    FIND=$(${tCATCMD} ${LINUXCONFIGFILE} | ${GREPBINARY} -v '^#' | ${GREPBINARY} "CONFIG_IP_NF_IPTABLES" | head -n 1)
                    if [ -n "${FIND}" ]; then
                        HAVEMOD=$(echo ${FIND} | ${CUTBINARY} -d '=' -f2)
                        # Do not use iptables if it's compiled as a module (=m), since we already tested for it in the
                        # active list.
                        if [ "${HAVEMOD}" = "y" ]; then
                            LogText "Result: iptables available as a module in the configuration"
                            IPTABLES_ACTIVE=1
                            IPTABLES_INKERNEL_ACTIVE=1
                            FIREWALL_ACTIVE=1
                            Display --indent 2 --text "- Checking iptables in config file" --result "${STATUS_FOUND}" --color GREEN
                        else
                            LogText "Result: no iptables found in Linux kernel config file"
                        fi
                    else
                        LogText "Result: no Linux configuration file found"
                        Display --indent 2 --text "- Checking iptables in config file" --result "${STATUS_NOT_FOUND}" --color WHITE
                    fi
                fi
            fi
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4508
    # Description : Check iptables chain policies
    # Notes       : Suggestions are currently disabled, until related page and documentation is available
    # TODO        : grep -z is not supported on BusyBox
    if [ ! "${IPTABLESBINARY}" = "" -a ${IPTABLES_ACTIVE} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4508 --preqs-met ${PREQS_MET} --os Linux --weight L --network NO --root-only YES --category security --description "Check used policies of iptables chains"
    if [ ${SKIPTEST} -eq 0 ]; then
        Display --indent 4 --text "- Checking iptables policies of chains" --result "${STATUS_FOUND}" --color GREEN
        IPTABLES_TABLES="filter nat mangle raw security"
        for IPTABLES_TABLE in ${IPTABLES_TABLES}
        do
            ${IPTABLESBINARY} -t "${IPTABLES_TABLE}" --list-rules --wait 1 2>/dev/zero |
            {
                IPTABLES_OUTPUT_QUEUE=""
                while IFS="$(printf '\n')" read -r IPTABLES_LINES
                do
                    set -- ${IPTABLES_LINES}
                    while [ $# -gt 0 ]; do
                        if [ "${1}" = "-P" ]; then
                            IPTABLES_CHAIN="${2}"
                            IPTABLES_TARGET="${3}"
                            shift 3
                        elif [ "${1}" = "-A" ] || [ "${1}" = "-N" ]; then
                            IPTABLES_CHAIN="${2}"
                            shift 2
                        elif [ "${1}" = "-j" ]; then
                            IPTABLES_TARGET="${2}"
                            shift
                        else
                            shift
                        fi
                    done
                    # logics
                    if [ "${IPTABLES_TABLE}" = "filter" ] || [ "${IPTABLES_TABLE}" = "security" ]; then
                        if [ "${IPTABLES_CHAIN}" = "INPUT" ]; then
                            if [ "${IPTABLES_TARGET}" = "ACCEPT" ]; then
                                IPTABLES_OUTPUT_QUEUE="${IPTABLES_OUTPUT_QUEUE}\n${IPTABLES_TABLE} ${IPTABLES_CHAIN} ${IPTABLES_TARGET} YELLOW 1 3"
                            elif [ "${IPTABLES_TARGET}" = "DROP" ]; then
                                IPTABLES_OUTPUT_QUEUE="${IPTABLES_OUTPUT_QUEUE}\n${IPTABLES_TABLE} ${IPTABLES_CHAIN} ${IPTABLES_TARGET} GREEN 3 3"
                            fi
                        fi
                        if [ "${IPTABLES_CHAIN}" = "INPUT" ] || [ "${IPTABLES_CHAIN}" = "FORWARD" ] || [ "${IPTABLES_CHAIN}" = "OUTPUT" ]; then
                            if [ "${IPTABLES_TARGET}" = "NFQUEUE" ]; then
                                IPTABLES_OUTPUT_QUEUE="${IPTABLES_OUTPUT_QUEUE}\n${IPTABLES_TABLE} ${IPTABLES_CHAIN} ${IPTABLES_TARGET} RED 0 3"
                            fi
                        fi
                    fi
                done
                if [ -n "${IPTABLES_OUTPUT_QUEUE}" ]; then
                    # Sort output if sort tool is available
                    if [ -n "${SORTBINARY}" ]; then
                        LogText "Info: sorting output"
                        IPTABLES_OUTPUT="$(printf '%b' "${IPTABLES_OUTPUT_QUEUE}" | ${SORTBINARY} -u )"
                    else
                        IPTABLES_OUTPUT="$(printf '%b' "${IPTABLES_OUTPUT_QUEUE}")"
                    fi
                    printf '%b\n' "${IPTABLES_OUTPUT}" | while IFS="$(printf '\n')" read -r IPTABLES_OUTPUT_LINE
                    do
                        if [ -n "$IPTABLES_OUTPUT_LINE" ]; then
                            set -- ${IPTABLES_OUTPUT_LINE}
                            while [ $# -gt 0 ]; do
                                LogText "Result: Found target '${3}' for chain '${2}' (table: ${1})"
                                Display --indent 6 --text "- Chain ${2} (table: ${1}, target: ${3})" --result "${3}" --color "${4}"
                                if [ "${3}" = "NFQUEUE" ]
                                then
                                    ReportSuggestion "${TEST_NO}" "Consider avoid ${3} target if possible (iptables chain ${2}, table: ${1})"
                                fi
                                AddHP "${5}" "${6}"
                                shift 6
                            done
                        fi
                    done
                fi
            }
            unset IPTABLES_TABLE
        done
        unset IPTABLES_TABLES
    fi
    unset PREQS_MET
#
#################################################################################
#
    # Test        : FIRE-4512
    # Description : Check iptables for empty ruleset (should have at least 5 or more rules)
    if [ -n "${IPTABLESBINARY}" -a ${IPTABLES_ACTIVE} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4512 --preqs-met ${PREQS_MET} --os Linux --weight L --network NO --root-only YES --category security --description "Check iptables for empty ruleset"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(${IPTABLESBINARY} --list --numeric 2> /dev/null | ${GREPBINARY} -E -v "^(Chain|target|$)" | ${WCBINARY} -l | ${TRBINARY} -d ' ')
        if [ -n "${FIND}" ]; then
            FIREWALL_ACTIVE=1
            if [ ${FIND} -le 5 ]; then
                # Firewall is active, but needs configuration
                FIREWALL_EMPTY_RULESET=1
                LogText "Result: iptables ruleset seems to be empty (found ${FIND} rules)"
                Display --indent 4 --text "- Checking for empty ruleset" --result "${STATUS_WARNING}" --color RED
                ReportWarning "${TEST_NO}" "iptables module(s) loaded, but no rules active"
            else
                LogText "Result: one or more rules are available (${FIND} rules)"
                Display --indent 4 --text "- Checking for empty ruleset" --result "${STATUS_OK}" --color GREEN
            fi
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4513
    # Description : Check iptables for unused rules
    if [ -n "${IPTABLESBINARY}" -a ${IPTABLES_ACTIVE} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4513 --preqs-met ${PREQS_MET} --os Linux --weight L --network NO --root-only YES --category security --description "Check iptables for unused rules"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(${IPTABLESBINARY} --list --numeric --line-numbers --verbose | ${AWKBINARY} '{ if ($2=="0") print $1 }' | ${XARGSBINARY})
        if IsEmpty "${FIND}"; then
            Display --indent 4 --text "- Checking for unused rules" --result "${STATUS_OK}" --color GREEN
            LogText "Result: There are no unused rules present"
        else
            Display --indent 4 --text "- Checking for unused rules" --result "${STATUS_FOUND}" --color YELLOW
            LogText "Result: Found one or more possible unused rules"
            LogText "Description: Unused rules can be a sign that the firewall rules aren't optimized or up-to-date"
            LogText "Note: Sometimes rules aren't triggered but still in use. Keep this in mind before cleaning up rules."
            LogText "Output: iptables rule numbers: ${FIND}"
            ReportSuggestion "${TEST_NO}" "Check iptables rules to see which rules are currently not used"
            LogText "Tip: iptables --list --numeric --line-numbers --verbose"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4514
    # Notes       :
    # Check if ipv6 is active on any network interface
    # If ip_tables is active, and ip6_tables is not, show warning about missing filtering
#
#################################################################################
#

    # Test        : FIRE-4518
    # Description : Checking status of pf firewall components
    # Notes       : Use /dev/pf as first detection method if pf is available
    if [ -e /dev/pf ]; then PREQS_MET="YES"; SKIPREASON=""; else PREQS_MET="NO"; SKIPREASON="No /dev/pf device"; fi
    Register --test-no FIRE-4518 --preqs-met ${PREQS_MET} --skip-reason "${SKIPREASON}" --weight L --network NO --root-only YES --category security --description "Check pf firewall components"
    if [ ${SKIPTEST} -eq 0 ]; then
        PFFOUND=0; PFLOGDFOUND=0

        # Check status with pfctl
        LogText "Test: checking pf status via pfctl"
        if [ -n "${PFCTLBINARY}" ]; then
            FIND=$(${PFCTLBINARY} -sa 2>&1 | ${GREPBINARY} "^Status" | ${HEADBINARY} -1 | ${AWKBINARY} '{ print $2 }')
            if [ "${FIND}" = "Disabled" ]; then
                if IsVerbose; then Display --indent 2 --text "- Checking pf status (pfctl)" --result "${STATUS_DISABLED}" --color RED; fi
                LogText "Result: pf is disabled"
                AddHP 0 3
            elif [ "${FIND}" = "Enabled" ]; then
                Display --indent 2 --text "- Checking pf status (pfctl)" --result "${STATUS_ENABLED}" --color GREEN
                LogText "Result: pf is enabled"
                PFFOUND=1
                AddHP 3 3
            else
                Display --indent 2 --text "- Checking pf status (pfctl)" --result "${STATUS_UNKNOWN}" --color YELLOW
                ReportException ${TEST_NO} "Unknown status of pf firewall"
            fi
        fi

        # If we didn't find the status to be enabled, stop searching
        if [ ${PFFOUND} -eq 0 ]; then
            # Check for pf kernel module (FreeBSD and similar)
            LogText "Test: searching for pf kernel module"
            if [ -n "${KLDSTATBINARY}" ]; then
                FIND=$(${KLDSTATBINARY} | ${GREPBINARY} 'pf.ko')
                if [ -z "${FIND}" ]; then
                    LogText "Result: Can not find pf KLD"
                else
                    LogText "Result: pf KLD loaded"
                    PFFOUND=1
                fi
            else
                LogText "Result: no kldstat binary, skipping this part"
            fi

            if IsRunning "pflogd"; then
                LogText "Result: found pflog daemon in process list"
                Display --indent 4 --text "- Checking pflogd status" --result "ACTIVE" --color GREEN
                PFFOUND=1
                PFLOGDFOUND=1
            else
                LogText "Result: pflog daemon not found in process list"
            fi
        fi

        if [ ${PFFOUND} -eq 1 ]; then
            FIREWALL_ACTIVE=1
            Report "firewall_software[]=pf"
        else
            LogText "Result: pf not running on this system"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4520
    # Description : Check pf configuration consistency
    if [ ${PFFOUND} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4520 --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Check pf configuration consistency"
    if [ ${SKIPTEST} -eq 0 ]; then
        LogText "Test: check /etc/pf.conf"
        # Test for warnings (-n don't load the rules)
        if [ -f /etc/pf.conf ]; then
            LogText "Result: /etc/pf.conf exists"
            # Check results from pfctl
            PFWARNINGS=$(${PFCTLBINARY} -n -f /etc/pf.conf -vvv 2>&1 | ${GREPBINARY} -i 'warning')
            if [ -z "${PFWARNINGS}" ]; then
                Display --indent 4 --text "- Checking pf configuration consistency" --result "${STATUS_OK}" --color GREEN
                LogText "Result: no pf filter warnings found"
            else
                Display --indent 4 --text "- Checking pf configuration consistency" --result "${STATUS_WARNING}" --color RED
                LogText "Result: found one or more warnings in the pf filter rules"
                ReportWarning "${TEST_NO}" "Found one or more warnings in pf configuration file" "/etc/pf.conf" "text:Run 'pfctl -n -f /etc/pf.conf -vvv' to see available pf warnings"
            fi
        else
            LogText "Result: /etc/pf.conf does NOT exist"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4522
    # Description : Check ipchains
#
#################################################################################
#
    # Test        : FIRE-4524
    # Description : Check for CSF (ConfigServer Security & Firewall)
    Register --test-no FIRE-4524 --weight L --network NO --category security --description "Check for CSF presence"
    if [ ${SKIPTEST} -eq 0 ]; then
        FILE="/etc/csf/csf.conf"
        LogText "Test: check ${FILE}"
        if [ -f ${FILE} ]; then
            LogText "Result: ${FILE} exists"
            FIREWALL_ACTIVE=1
            Report "firewall_software[]=csf"
            Display --indent 2 --text "- Checking CSF status (configuration file)" --result "${STATUS_FOUND}" --color GREEN

            LogText "Test: check if CSF testing mode is disabled"
            FIND=$(${GREPBINARY} -P "^TESTING(\s|=)" ${FILE} | ${CUTBINARY} -d= -f2 | ${XARGSBINARY})
            if [ "${FIND}" = "0" ]; then
                Display --indent 4 --text "- Check if CSF testing mode is disabled" --result "${STATUS_OK}" --color GREEN
            else
                Display --indent 4 --text "- Check if CSF testing mode is disabled" --result "${STATUS_WARNING}" --color RED
            fi

            LogText "Test: check if CSF is running"
            if [ ! -f /etc/csf/csf.disable ]; then
                Display --indent 4 --text "- Check if CSF is running" --result "${STATUS_OK}" --color GREEN
            else
                Display --indent 4 --text "- Check if CSF is running" --result "${STATUS_WARNING}" --color RED
            fi
        else
            LogText "Result: ${FILE} does NOT exist"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4526
    # Description : Check ipf (Solaris)
    if [ ! "${IPFBINARY}" = "" ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4526 --os Solaris --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Check ipf status"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(${IPFBINARY} -n -V | ${GREPBINARY} "^Running" | ${AWKBINARY} '{ print $2 }')
        if [ "${FIND}" = "yes" ]; then
            Display --indent 4 --text "- Checking ipf status" --result "${STATUS_RUNNING}" --color GREEN
            LogText "Result: ipf is enabled and running"
            FIREWALL_ACTIVE=1
            Report "firewall_software[]=ipf"
        else
            Display --indent 4 --text "- Checking ipf status" --result "${STATUS_NOT_RUNNING}" --color YELLOW
            LogText "Result: ipf is not running"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4530
    # Description : Check IPFW (FreeBSD)
    Register --test-no FIRE-4530 --os FreeBSD --weight L --network NO --category security --description "Check IPFW status"
    if [ ${SKIPTEST} -eq 0 ]; then
        if [ -n "${SYSCTLBINARY}" ]; then
            # For now, only check for IPv4.
            FIND=$(${SYSCTLBINARY} net.inet.ip.fw.enable 2> /dev/null | ${AWKBINARY} '{ print $2 }')
            if [ "${FIND}" = "1" ]; then
                Display --indent 2 --text "- Checking IPFW status" --result "${STATUS_RUNNING}" --color GREEN
                LogText "Result: IPFW is running for IPv4"
                FIREWALL_ACTIVE=1
                Report "firewall_software[]=ipfw"
                IPFW_ENABLED=$(service -e | ${GREPBINARY} -o ipfw)
                if [ "${IPFW_ENABLED}" = "ipfw" ]; then
                    Display --indent 4 --text "- IPFW enabled in /etc/rc.conf" --result "${STATUS_YES}" --color GREEN
                    LogText "Result: IPFW is enabled at start-up for IPv4"
                else
                    Display --indent 4 --text "- ipfw enabled in /etc/rc.conf" --result "${STATUS_NO}" --color YELLOW
                    LogText "Result: IPFW is disabled at start-up for IPv4"
                fi
            else
                if IsVerbose; then Display --indent 2 --text "- Checking IPFW status" --result "${STATUS_NOT_RUNNING}" --color YELLOW; fi
                LogText "Result: IPFW is not running for IPv4"
            fi
        else
            ReportException "${TEST_NO}:1" "No IPFW test available (sysctl missing)"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4532
    # Description : Check Application Firewall in macOS
    if [ -x /usr/libexec/ApplicationFirewall/socketfilterfw ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4532 --weight L --os "macOS" --preqs-met ${PREQS_MET} --network NO --category security --description "Check macOS application firewall"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2> /dev/null | ${GREPBINARY} "Firewall is enabled")
        if [ -n "${FIND}" ]; then
            Display --indent 2 --text "- Checking macOS: Application Firewall" --result "${STATUS_ENABLED}" --color GREEN
            AddHP 3 3
            LogText "Result: application firewall of macOS is enabled"
            FIREWALL_ACTIVE=1
            APPLICATION_FIREWALL_ACTIVE=1
            Report "firewall_software[]=macosx-app-fw"
            Report "app_fw[]=macosx-app-fw"
        else
            if IsVerbose; then Display --indent 2 --text "- Checking macOS: Application Firewall" --result "${STATUS_DISABLED}" --color YELLOW; fi
            AddHP 1 3
            LogText "Result: application firewall of macOS is disabled"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4534
    # Description : Check outbound firewalls on macOS
    Register --test-no FIRE-4534 --weight L --os "macOS" --network NO --category security --description "Check for presence of outbound firewalls on macOS"
    if [ ${SKIPTEST} -eq 0 ]; then

        FOUND=0

        # Little Snitch Daemon (macOS)
        LogText "Test: checking process Little Snitch Daemon"
        if IsRunning --full "Little Snitch Daemon"; then
            Display --indent 2 --text "- Checking Little Snitch Daemon" --result "${STATUS_ENABLED}" --color GREEN
            LogText "Result: Little Snitch found"
            FOUND=1
            FIREWALL_ACTIVE=1
            APPLICATION_FIREWALL_ACTIVE=1
            Report "app_fw[]=little-snitch"
            Report "firewall_software[]=little-snitch"
        fi

        # HandsOff! Daemon (macOS)
        LogText "Test: checking process HandsOffDaemon"
        if IsRunning "HandsOffDaemon"; then
            Display --indent 2 --text "- Checking Hands Off! Daemon" --result "${STATUS_ENABLED}" --color GREEN
            LogText "Result: Hands Off! found"
            FOUND=1
            FIREWALL_ACTIVE=1
            APPLICATION_FIREWALL_ACTIVE=1
            Report "app_fw[]=hands-off"
            Report "firewall_software[]=hands-off"
        fi

        # LuLu Daemon (macOS)
        LogText "Test: checking process LuLu"
        if IsRunning "LuLu"; then
            Display --indent 2 --text "- Checking LuLu Daemon" --result "${STATUS_ENABLED}" --color GREEN
            LogText "Result: LuLu found"
            FOUND=1
            FIREWALL_ACTIVE=1
            APPLICATION_FIREWALL_ACTIVE=1
            Report "app_fw[]=lulu"
            Report "firewall_software[]=lulu"
        fi

        # Radio Silence (macOS)
        LogText "Test: checking process Radio Silence"
        if IsRunning --full "Radio Silence"; then
            Display --indent 2 --text "- Checking Radio Silence" --result "${STATUS_ENABLED}" --color GREEN
            LogText "Result: Radio Silence found"
            FOUND=1
            FIREWALL_ACTIVE=1
            APPLICATION_FIREWALL_ACTIVE=1
            Report "app_fw[]=radio-silence"
            Report "firewall_software[]=radio-silence"
        fi

        if [ ${FOUND} -eq 0 ]; then
            LogText "Result: outbound firewall not found"
            AddHP 1 3
        else
            LogText "Result: found one or more macOS outbound firewall"
            AddHP 3 3
        fi

    fi
#
#################################################################################
#
    # Test        : FIRE-4536
    # Description : Check nftables kernel module
    if HasData "${NFTBINARY}"; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4536 --os Linux --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Check nftables status"
    if [ ${SKIPTEST} -eq 0 ]; then
        FIND=$(${LSMODBINARY} | ${AWKBINARY} '{ print $1 }' | ${GREPBINARY} "^nf*_tables")
        if [ -n "${FIND}" ]; then
            LogText "Result: found nftables kernel module"
            FIREWALL_ACTIVE=1
            NFTABLES_ACTIVE=1
            Report "firewall_software[]=nftables"
        else
            LogText "Result: no nftables kernel module found"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4538
    # Description : Check nftables configuration
    if HasData "${NFTBINARY}"; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4538 --os Linux --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Check nftables basic configuration"
    if [ ${SKIPTEST} -eq 0 ]; then
        # Retrieve nft version
        NFT_VERSION=$(${NFTBINARY} --version 2> /dev/null | ${AWKBINARY} '{ if ($1=="nftables") { print $2 }}' | ${TRBINARY} -d 'v')
        Report "nft_version=${NFT_VERSION}"
        LogText "Result: found version ${NFT_VERSION} of nft"
    fi
#
#################################################################################
#
    # Test        : FIRE-4540
    # Description : Check nftables configuration
    if HasData "${NFTBINARY}"; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4540 --os Linux --preqs-met ${PREQS_MET} --weight L --network NO --root-only YES --category security --description "Check for empty nftables configuration"
    if [ ${SKIPTEST} -eq 0 ]; then
        # Check for empty ruleset
        NFT_RULES_LENGTH=$(${NFTBINARY} --stateless list ruleset 2> /dev/null | ${GREPBINARY} -E -v "table|chain|;$|}$|^$" | ${WCBINARY} -l)
        if [ ${NFT_RULES_LENGTH} -le 3 ]; then
            FIREWALL_EMPTY_RULESET=1
            LogText "Result: this firewall set has 3 rules or less and is considered to be empty"
        else
            LogText "Result: found ${NFT_RULES_LENGTH} rules in nftables configuration"
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4586
    # Description : Check firewall logging
    if [ ${FIREWALL_ACTIVE} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    Register --test-no FIRE-4586 --preqs-met ${PREQS_MET} --weight L --network NO --root-only YES --category security --description "Check firewall logging"
    if [ ${SKIPTEST} -eq 0 ]; then
        if [ ${IPTABLES_ACTIVE} -eq 1 ]; then
            if [ -n "${IPTABLESSAVEBINARY}" ]; then
                HAS_LOGGING=$(${IPTABLESSAVEBINARY} | ${EGREPBINARY} "\-j (NF)?LOG")
                if [ -z "${HAS_LOGGING}" ]; then
                    Report "firewall_no_logging[]=iptables"
                fi
             fi
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4590
    # Description : Check if at least one firewall if active
    Register --test-no FIRE-4590 --weight L --network NO --category security --description "Check firewall status"
    if [ ${SKIPTEST} -eq 0 ]; then
        if [ ${FIREWALL_ACTIVE} -eq 1 ]; then
            Display --indent 2 --text "- Checking host based firewall" --result "${STATUS_ACTIVE}" --color GREEN
            LogText "Result: host based firewall or packet filter is active"
            Report "manual[]=Verify if there is a formal process for testing and applying firewall rules"
            Report "manual[]=Verify all traffic is filtered the right way between the different security zones"
            Report "manual[]=Verify if a list is available with all required services"
            # YYY Solaris ipf (determine default policy)
            Report "manual[]=Make sure an explicit deny all is the default policy for all unmatched traffic"
            AddHP 5 5
        else
            Display --indent 2 --text "- Checking host based firewall" --result "${STATUS_NOT_ACTIVE}" --color YELLOW
            LogText "Result: no host based firewall/packet filter found or configured"
            ReportSuggestion "${TEST_NO}" "Configure a firewall/packet filter to filter incoming and outgoing traffic"
            AddHP 0 5
        fi
    fi
#
#################################################################################
#
    # Test        : FIRE-4594
    # Description : Check for APF (Advanced Policy Firewall)
    Register --test-no FIRE-4594 --weight L --network NO --category security --description "Check for APF presence"
    if [ -n "${IPTABLESBINARY}" ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi
    if [ ${SKIPTEST} -eq 0 ]; then
        FILE="/etc/apf/conf.apf"
        LogText "Test: check ${FILE}"
        if [ -f ${FILE} ]; then
            LogText "Result: ${FILE} exists"
            FIREWALL_ACTIVE=1
            Report "firewall_software[]=apf"
            Display --indent 2 --text "- Checking APF status (configuration file)" --result "${STATUS_FOUND}" --color GREEN

            LogText "Test: check if APF testing mode is disabled"
            FIND=$(${GREPBINARY} -P "^DEVEL_MODE(\s|=)" ${FILE} | ${CUTBINARY} -d= -f2 | ${XARGSBINARY})
            if [ "${FIND}" = "0" ]; then
                Display --indent 4 --text "- Check if APF testing mode is disabled" --result "${STATUS_OK}" --color GREEN
            else
                Display --indent 4 --text "- Check if APF testing mode is disabled" --result "${STATUS_WARNING}" --color RED
            fi

            LogText "Test: check if APF is running"
            FIND=$(${IPTABLESBINARY} -L -n | ${GREPBINARY} -iom1 sanity | ${WCBINARY} -l)
            if [ "${FIND}" = "1" ]; then
                Display --indent 4 --text "- Check if APF is running" --result "${STATUS_OK}" --color GREEN
            else
                Display --indent 4 --text "- Check if APF is running" --result "${STATUS_WARNING}" --color RED
            fi
        else
            LogText "Result: ${FILE} does NOT exist"
        fi
    fi
#
#################################################################################
#

WaitForKeyPress

#
#################################################################################
#
    # TODO
    # Suggestion to disable iptables if nftables is enabled
    # Check for specific features in nftables releases
#
#================================================================================
# Lynis - Security Auditing and System Hardening for Linux and UNIX - https://cisofy.com
