#!/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.
#
#################################################################################
#
# Shells
#
#################################################################################
#
    IDLE_TIMEOUT=0
    InsertSection "${SECTION_SHELLS}"
#
#################################################################################
#
    # bash
    # Files (interactive login shells):     /etc/profile $HOME/.bash_profile
    #                                       $HOME/.bash_login $HOME/.profile
    # Files (interactive non-login shells): $HOME/.bash_rc
    #
    # csh/tcsh
    # Files: /etc/csh.cshrc /etc/csh.login
    #
    # zsh
    # Files: /etc/zshenv /etc/zsh/zshenv $HOME/.zshenv /etc/zprofile
    #        /etc/zsh/zprofile $HOME/.zprofile /etc/zshrc /etc/zsh/zshrc
    #        $ZDOTDIR/.zshrc /etc/zlogin /etc/zsh/zlogin

    SHELL_LOGIN_FILES="${ROOTDIR}etc/csh.cshrc ${ROOTDIR}etc/csh.login ${ROOTDIR}etc/zshenv ${ROOTDIR}etc/zsh/zshenv
                       ${ROOTDIR}etc/zprofile ${ROOTDIR}etc/zsh/zprofile ${ROOTDIR}etc/zshrc ${ROOTDIR}etc/zsh/zshrc
                       ${ROOTDIR}etc/zlogin ${ROOTDIR}etc/zsh/zlogin"
#
#################################################################################
#

    # Test        : SHLL-6202
    # Description : check all console TTYs in which root user can enter single user mode without password
    Register --test-no SHLL-6202 --os FreeBSD --weight L --network NO --category security --description "Check console TTYs"
    if [ ${SKIPTEST} -eq 0 ]; then
        LogText "Test: Checking console TTYs"
        FIND=$(${GREPBINARY} -E '^console' ${ROOTDIR}etc/ttys | ${GREPBINARY} -v 'insecure')
        if [ -z "${FIND}" ]; then
            Display --indent 2 --text "- Checking console TTYs" --result "${STATUS_OK}" --color GREEN
            LogText "Result: console is secured against single user mode without password."
        else
            Display --indent 2 --text "- Checking console TTYs" --result "${STATUS_WARNING}" --color RED
            LogText "Result: Found insecure console in ${ROOTDIR}etc/ttys. Single user mode login without password allowed!"
            LogText "Output ${ROOTDIR}etc/ttys:"
            LogText "${FIND}"
            ReportWarning "${TEST_NO}" "Found unprotected console in ${ROOTDIR}etc/ttys"
            LogText "Possible solution: Change the console line from 'secure' to 'insecure'."
        fi
    fi
#
#################################################################################
#
    # Test        : SHLL-6211
    # Description : Determine available shell according /etc/shells
    Register --test-no SHLL-6211 --weight L --network NO --category security --description "Available and valid shells"
    if [ ${SKIPTEST} -eq 0 ]; then
        LogText "Test: Searching for ${ROOTDIR}etc/shells"
        if [ -f ${ROOTDIR}etc/shells ]; then
            LogText "Result: Found ${ROOTDIR}etc/shells file"
            LogText "Test: Reading available shells from ${ROOTDIR}etc/shells"
            SSHELLS=$(${GREPBINARY} "^/" ${ROOTDIR}etc/shells)
            CSSHELLS=0; CSSHELLS_ALL=0
            Display --indent 2 --text "- Checking shells from ${ROOTDIR}etc/shells"
            for I in ${SSHELLS}; do
                CSSHELLS_ALL=$((CSSHELLS_ALL + 1))
                Report "available_shell[]=${I}"
                # TODO add check for symlinked shells
                if [ -f ${I} ]; then
                    LogText "Found installed shell: ${I}"
                    CSSHELLS=$((CSSHELLS + 1))
                else
                    LogText "Shell ${I} not installed. Probably a dummy or non existing shell."
                fi
            done
            Display --indent 4 --text "Result: found ${CSSHELLS_ALL} shells (valid shells: ${CSSHELLS})."
        else
            LogText "Result: ${ROOTDIR}etc/shells not found, skipping test"
        fi
    fi
#
#################################################################################
#
    # Test        : SHLL-6220
    # Description : Check for idle session killing tools or settings
    Register --test-no SHLL-6220 --weight L --network NO --category security --description "Idle session killing tools or settings"
    if [ ${SKIPTEST} -eq 0 ]; then

        IDLE_TIMEOUT_METHOD=""
        IDLE_TIMEOUT_READONLY=""

        LogText "Test: Search for session timeout tools or settings in shell"
        if IsRunning "timeoutd"; then
            IDLE_TIMEOUT=1
            LogText "Result: found timeoutd process to kill idle sessions"
            IDLE_TIMEOUT_METHOD="timeout-daemon"
        fi
        if IsRunning "autolog"; then
            IDLE_TIMEOUT=1
            LogText "Result: found autolog process to kill idle sessions"
            Report "session_timeout_method[]=autolog"
            IDLE_TIMEOUT_METHOD="autolog"
        fi

        if [ -f ${ROOTDIR}etc/profile ]; then
            # Determine if we can find a TMOUT value
            FIND=$(${GREPBINARY} 'TMOUT=' ${ROOTDIR}etc/profile | ${TRBINARY} -d ' ' | ${TRBINARY} -d '\t' | ${GREPBINARY} -v "^#" | ${SEDBINARY} 's/export//' | ${SEDBINARY} 's/#.*//' | ${AWKBINARY} -F= '{ print $2 }')
            # Determine if the value is exported (with export, readonly, or typeset)
            FIND2=$(${GREPBINARY} '\(export\|readonly\|typeset -r\)[ \t]*TMOUT' ${ROOTDIR}etc/profile | ${GREPBINARY} -v "^#" | ${SEDBINARY} 's/#.*//' | ${AWKBINARY} '{ print $1 }')
            if [ -n "${FIND}" ]; then
                N=0; IDLE_TIMEOUT=1
                for I in ${FIND}; do
                    LogText "Output: ${I}"
                    Report "session_timeout_value[]=${I}"
                    N=$((N + 1))
                done
                if [ ${N} -eq 1 ]; then
                    LogText "Result: found TMOUT value configured in ${ROOTDIR}etc/profile"
                else
                    LogText "Result: found several TMOUT values configured in ${ROOTDIR}etc/profile"
                fi
                IDLE_TIMEOUT_METHOD="profile"
            else
                LogText "Result: could not find TMOUT setting in ${ROOTDIR}etc/profile"
            fi

            if [ -n "${FIND2}" ]; then
                N=0;
                for I in ${FIND2}; do
                    LogText "Output: ${I}"
                    if [ "${I}" = "readonly" -o "${I}" = "typeset" ]; then
                        N=$((N + 1))
                    fi
                done
                if [ ${N} -gt 0 ]; then
                    LogText "Result: found readonly setting in ${ROOTDIR}etc/profile (readonly or typeset -r)"
                    IDLE_TIMEOUT_READONLY=1
                else
                    LogText "Result: NO readonly setting found in ${ROOTDIR}etc/profile (readonly or typeset -r)"
                    IDLE_TIMEOUT_READONLY=0
                fi
            else
                LogText "Result: could not find export, readonly or typeset -r in ${ROOTDIR}etc/profile"
            fi
        else
            LogText "Result: skip ${ROOTDIR}etc/profile test, file not available on this system"
        fi

        if [ -d ${ROOTDIR}etc/profile.d ]; then
            FIND=$(${LSBINARY} ${ROOTDIR}etc/profile.d/*.sh 2> /dev/null)
            if [ -n "${FIND}" ]; then
                # Determine if we can find a TMOUT value
                FIND=$(${FINDBINARY} -L ${ROOTDIR}etc/profile.d -name "*.sh" -type f -exec cat {} \; 2> /dev/null | ${GREPBINARY} 'TMOUT=' | ${TRBINARY} -d ' ' | ${TRBINARY} -d '\t' | ${GREPBINARY} -v "^#" | ${SEDBINARY} 's/export//' | ${SEDBINARY} 's/#.*//' | ${AWKBINARY} -F= '{ print $2 }')
                # Determine if the value is exported (with export, readonly, or typeset)
                FIND2=$(${FINDBINARY} -L ${ROOTDIR}etc/profile.d -name "*.sh" -type f -exec cat {} \; 2> /dev/null | ${GREPBINARY} '\(export\|readonly\|typeset -r\)[ \t]*TMOUT' | ${GREPBINARY} -v "^#" | ${SEDBINARY} 's/#.*//' | ${AWKBINARY} '{ print $1 }')
                if [ -n "${FIND}" ]; then
                    N=0; IDLE_TIMEOUT=1
                    for I in ${FIND}; do
                        LogText "Output: ${I}"
                        Report "session_timeout_value[]=${I}"
                        N=$((N + 1))
                    done
                    if [ ${N} -eq 1 ]; then
                        LogText "Result: found TMOUT value configured in one of the files in ${ROOTDIR}etc/profile.d directory"
                    else
                        LogText "Result: found several TMOUT values configured in one of the files in ${ROOTDIR}etc/profile.d directory"
                    fi
                    IDLE_TIMEOUT_METHOD="profile.d"
                else
                    LogText "Result: could not find TMOUT setting in ${ROOTDIR}etc/profile.d/*.sh"
                fi
                # Check for readonly
                if [ -n "${FIND2}" ]; then
                    N=0;
                    for I in ${FIND2}; do
                        LogText "Output: ${I}"
                        if [ "${I}" = "readonly" -o "${I}" = "typeset" ]; then
                            N=$((N + 1))
                        fi
                    done
                    if [ ${N} -gt 0 ]; then
                        LogText "Result: found readonly setting in ${ROOTDIR}etc/profile (readonly or typeset -r)"
                        IDLE_TIMEOUT_READONLY=1
                    else
                        LogText "Result: NO readonly setting found in ${ROOTDIR}etc/profile (readonly or typeset -r)"
                        IDLE_TIMEOUT_READONLY=0
                    fi
                else
                    LogText "Result: could not find export, readonly or typeset -r in ${ROOTDIR}etc/profile"
                fi
            fi
        else
            LogText "Result: skip ${ROOTDIR}etc/profile.d directory test, directory not available on this system"
        fi

        if [ -n "${IDLE_TIMEOUT_METHOD}" ]; then
            Report "session_timeout_method[]=${IDLE_TIMEOUT_METHOD}"
        fi
        if [ -n "${IDLE_TIMEOUT_READONLY}" ]; then
            Report "session_timeout_set_readonly=${IDLE_TIMEOUT_READONLY}"
        fi

        if [ ${IDLE_TIMEOUT} -eq 1 ]; then
            Display --indent 4 --text "- Session timeout settings/tools" --result "${STATUS_FOUND}" --color GREEN
            AddHP 3 3
        else
            Display --indent 4 --text "- Session timeout settings/tools" --result "${STATUS_NONE}" --color YELLOW
            AddHP 1 3
        fi
    fi
#
#################################################################################
#
    # Test        : SHLL-6230
    # Description : Check for umask values in shell configurations
    SHELL_CONFIG_FILES="${ROOTDIR}etc/bashrc ${ROOTDIR}etc/bash.bashrc ${ROOTDIR}etc/bash.bashrc.local ${ROOTDIR}etc/csh.cshrc ${ROOTDIR}etc/profile"
    Register --test-no SHLL-6230 --weight H --network NO --category security --description "Perform umask check for shell configurations"
    if [ ${SKIPTEST} -eq 0 ]; then
        FOUND=0
        Display --indent 2 --text "- Checking default umask values"
        for FILE in ${SHELL_CONFIG_FILES}; do
            HARDENING_POSSIBLE=0
            FIND=""
            if [ -f ${FILE} ]; then
                LogText "Result: file ${FILE} exists"
                FOUND=1
                FIND=$(${GREPBINARY} umask ${FILE} | ${SEDBINARY} 's/^[ \t]*//g' | ${SEDBINARY} 's/#.*$//' | ${GREPBINARY} -v "^$" | ${AWKBINARY} '{ print $2 }')
                if IsEmpty "${FIND}"; then
                    LogText "Result: did not find umask configured in ${FILE}"
                    Display --indent 4 --text "- Checking default umask in ${FILE}" --result "${STATUS_NONE}" --color YELLOW
                else
                    for UMASKVALUE in ${FIND}; do
                        LogText "Result: found umask ${UMASKVALUE} in ${FILE}"
                        case ${UMASKVALUE} in
                            027|0027|077|0077)
                                    LogText "Result: umask ${UMASKVALUE} is considered a properly hardened value"
                            ;;
                            *)
                                    LogText "Result: umask ${UMASKVALUE} can be hardened "
                                    HARDENING_POSSIBLE=1
                            ;;
                        esac
                    done
                    if [ ${HARDENING_POSSIBLE} -eq 0 ]; then
                        Display --indent 4 --text "- Checking default umask in ${FILE}" --result "${STATUS_OK}" --color GREEN
                        AddHP 3 3
                    else
                        Display --indent 4 --text "- Checking default umask in ${FILE}" --result "${STATUS_WEAK}" --color YELLOW
                        AddHP 1 3
                    fi
                fi
            else
                LogText "Result: file ${FILE} not found"
            fi
        done
    fi
#
#################################################################################
#

Report "session_timeout_enabled=${IDLE_TIMEOUT}"

#
#################################################################################
#

WaitForKeyPress

# EOF
