mirror of
				https://github.com/CISOfy/lynis.git
				synced 2025-10-31 03:14:50 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2179 lines
		
	
	
		
			87 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			2179 lines
		
	
	
		
			87 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| #!/bin/sh
 | |
| 
 | |
| #################################################################################
 | |
| #
 | |
| #   Lynis
 | |
| # ------------------
 | |
| #
 | |
| # Copyright 2007-2013, Michael Boelen
 | |
| # Copyright 2013-2016, CISOfy
 | |
| #
 | |
| # Website  : https://cisofy.com
 | |
| # Blog     : http://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.
 | |
| #
 | |
| #################################################################################
 | |
| #
 | |
| # Functions
 | |
| #
 | |
| #################################################################################
 | |
| #
 | |
| #    Function                   Description
 | |
| #    -----------------------    -------------------------------------------------
 | |
| #    AddHP                      Add Hardening points to plot a graph later
 | |
| #    AddSystemGroup             Adds a system to a group
 | |
| #    CheckFilePermissions       Check file permissions
 | |
| #    CheckUpdates               Determine if a new version of Lynis is available
 | |
| #    CreateTempFile             Create a temporary file
 | |
| #    counttests                 Count number of performed tests
 | |
| #    ContainsString             Find the needle (string) in the haystack (another string)
 | |
| #    Debug                      Display additional information on the screen (not suited for cronjob)
 | |
| #    DigitsOnly                 Return only the digits from a string
 | |
| #    DirectoryExists            Check if a directory exists on the disk
 | |
| #    Display                    Output text to screen with colors and identation
 | |
| #    DisplayManual              Output text to screen without any layout
 | |
| #    ExitClean                  Stop the program (cleanly), with exit code 0
 | |
| #    ExitCustom                 Stop the program (cleanly), with custom exit code
 | |
| #    ExitFatal                  Stop the program (cleanly), with exit code 1
 | |
| #    FileExists                 Check if a file exists on the disk
 | |
| #    FileIsEmpty                Check if a file is empty
 | |
| #    FileIsReadable             Check if a file is readable or directory accessible
 | |
| #    GetHostID                  Retrieve an unique ID for this host
 | |
| #    IsRunning                  Check if a process is running
 | |
| #    InsertSection              Insert a section block
 | |
| #    InsertPluginSection        Insert a section block for plugins
 | |
| #    IsVirtualMachine           Check if this system is a virtual machine
 | |
| #    IsWorldExecutable          Check if a file is world executable
 | |
| #    IsWorldReadable            Check if a file is world readable
 | |
| #    IsWorldWritable            Check if a file is world writable
 | |
| #    LogText                    Log text strings to logfile, prefixed with date/time
 | |
| #    ParseNginx                 Parse nginx configuration lines
 | |
| #    PortIsListening            Check if machine is listening on specified protocol and port
 | |
| #    Progress                   Show progress on screen
 | |
| #    RandomString               Show a random string
 | |
| #    RemovePIDFile              Remove PID file
 | |
| #    RemoveTempFiles            Remove temporary files
 | |
| #    Report                     Add string of data to report file
 | |
| #    ReportDetails              Store details of tests which include smaller atomic tests in report
 | |
| #    ReportException            Add an exception to the report file (for debugging purposes)
 | |
| #    ReportSuggestion           Add a suggestion to report file
 | |
| #    ReportWarning              Add a warning and priority to report file
 | |
| #    Register                   Register a test (for logging and execution)
 | |
| #    SafePerms                  Check if a directory has safe permissions
 | |
| #    SearchItem                 Search a string in a file
 | |
| #    ShowComplianceFinding      Display a particular finding regarding compliance or a security standard
 | |
| #    ShowSymlinkPath            Show a path behind a symlink
 | |
| #    SkipAtomicTest             Test if a subtest needs to be skipped
 | |
| #    TestValue                  Evaluate a value in a string or key
 | |
| #    ViewCategories             Display tests categories
 | |
| #    WaitForKeypress            Wait for user to press a key to continue
 | |
| 
 | |
| #    TestCase_Equal             Test case for checking if value is equal to something
 | |
| #    TestCase_NotEqual          Test case for checking if value is not equal to something
 | |
| #    TestCase_GreaterThan       Test case for checking if value is greater than something
 | |
| #    TestCase_GreaterOrEqual    Test case for checking if value is greater or equal to something
 | |
| #    TestCase_LessThan          Test case for checking if value is less than something
 | |
| #    TestCase_LessOrEqual       Test case for checking if value is less or equal to something
 | |
| #
 | |
| #################################################################################
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : AddHP()
 | |
|     # Description : Add Hardening Points
 | |
|     # Returns     : <nothing>
 | |
|     ################################################################################
 | |
| 
 | |
|     AddHP() {
 | |
|         HPADD=$1; HPADDMAX=$2
 | |
|         HPPOINTS=`expr ${HPPOINTS} + ${HPADD}`
 | |
|         HPTOTAL=`expr ${HPTOTAL} + ${HPADDMAX}`
 | |
|         if [ ${HPADD} -eq ${HPADDMAX} ]; then
 | |
|             LogText "Hardening: assigned maximum number of hardening points for this item (${HPADDMAX}). Currently having ${HPPOINTS} points (out of ${HPTOTAL})"
 | |
|           else
 | |
|             LogText "Hardening: assigned partial number of hardening points (${HPADD} of ${HPADDMAX}). Currently having ${HPPOINTS} points (out of ${HPTOTAL})"
 | |
|         fi
 | |
|     }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : AddSystemGroup
 | |
|     # Description : Adds a system to a group, which can be used for categorizing
 | |
|     # Returns     : <nothing>
 | |
|     ################################################################################
 | |
| 
 | |
|     AddSystemGroup()
 | |
|       {
 | |
|         Report "system_group[]=$1"
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Check file permissions
 | |
|     # Parameter 1 is file/dir
 | |
|     # Result: FILE_NOT_FOUND | OK | BAD
 | |
|     CheckFilePermissions()
 | |
|       {
 | |
|         CHECKFILE=$1
 | |
|         if [ ! -d $CHECKFILE -a ! -f $CHECKFILE ]; then
 | |
|             PERMS="FILE_NOT_FOUND"
 | |
|           else
 | |
|             # If 'file' is an directory, use -d
 | |
|             if [ -d ${CHECKFILE} ]; then
 | |
|                 FILEVALUE=`ls -d -l ${CHECKFILE} | cut -c 2-10`
 | |
|                 PROFILEVALUE=`grep '^permdir' ${PROFILE} | grep ":${CHECKFILE}:" | cut -d: -f3`
 | |
|               else
 | |
|                 FILEVALUE=`ls -l ${CHECKFILE} | cut -c 2-10`
 | |
|                 PROFILEVALUE=`grep '^permfile' ${PROFILE} | grep ":${CHECKFILE}:" | cut -d: -f3`
 | |
|             fi
 | |
|             if [ "${FILEVALUE}" = "${PROFILEVALUE}" ]; then PERMS="OK"; else PERMS="BAD"; fi
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : CheckItem()
 | |
|     # Description : Check if a specific item exists in the report
 | |
|     # Returns     : <nothing>
 | |
|     ################################################################################
 | |
| 
 | |
|     CheckItem()
 | |
|       {
 | |
|          ITEM_FOUND=0
 | |
|          if [ $# -eq 2 ]; then
 | |
|             # Don't search in /dev/null, it's too empty there
 | |
|             if [ ! "${REPORTFILE}" = "/dev/null" ]; then
 | |
|                 # Check if we can find the main type (with or without brackets)
 | |
|                 LogText "Test: search string $2 in earlier discovered results"
 | |
|                 FIND=`egrep "^$1(\[\])?=" ${REPORTFILE} | egrep "$2"`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     ITEM_FOUND=1
 | |
|                     LogText "Result: found search string (result: $FIND)"
 | |
|                  else
 | |
|                     LogText "Result: search string NOT found"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Skipping search, as /dev/null is being used"
 | |
|             fi
 | |
|           else
 | |
|             ReportException ${TEST_NO} "Error in function call to CheckItem"
 | |
|          fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : ContainsString()
 | |
|     # Description : Search a specific string (or regular expression) in another
 | |
|     # Returns     : (0 - True, 1 - False)
 | |
|     ################################################################################
 | |
| 
 | |
|     ContainsString() {
 | |
|         if [ $# -ne 2 ]; then ReportException "ContainsString" "Incorrect number of arguments for ContainsStrings function"; fi
 | |
|         RETVAL=1
 | |
|         local FIND=`echo "$2" | egrep "$1"`
 | |
|         if [ ! "${FIND}" = "" ]; then RETVAL=0; fi
 | |
| 
 | |
|         return $RETVAL
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_Equal()
 | |
|     # Description : Test case for checking if value whether value is equal to value
 | |
|     # Returns     : (0 - SUCCESS; 1 - MEDIUM-VALUED; 2 - FAIL)
 | |
|     ################################################################################
 | |
| 
 | |
|     TestCase_Equal()
 | |
|     {
 | |
|         local RETVAL=2
 | |
| 
 | |
|         if [ "$#" -lt "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to ${FUNCNAME}"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
| 
 | |
|             LogText "${FUNCNAME}: check if ${1} is equal to ${2}"
 | |
| 
 | |
|             if [ "$1" == "$2" ]; then
 | |
|               LogText "${FUNCNAME}: ${1} is equal to ${2}"
 | |
|               RETVAL=0
 | |
|             fi
 | |
| 
 | |
|             if ! [ -z ${3+x} ]; then
 | |
|                 LogText "${FUNCNAME}: ${1} is equal to ${3}"
 | |
|                 if [ "$2" == "$3" ]; then
 | |
|                     LogText "${FUNCNAME}: ${OPTION} is equal to ${3}"
 | |
|                     RETVAL=1
 | |
|                 fi
 | |
|             fi
 | |
|         fi
 | |
|         return ${RETVAL}
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_ValueNotEqual()
 | |
|     # Description : Test case for checking if value is not equal to something
 | |
|     # Returns     : (0 - SUCCESS;  1 - FAIL)
 | |
|     ################################################################################
 | |
|     TestCase_NotEqual()
 | |
|     {
 | |
|         local RETVAL=1
 | |
|         if [ "$#" -ne "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to ${FUNCNAME}"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
|             if [ "$1" != "$2" ]; then
 | |
|               LogText "${FUNCNAME}: ${1} is not equal to ${2}"
 | |
|               RETVAL=0
 | |
|             else
 | |
|               LogText "${FUNCNAME}: ${1} is equal to ${2}"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         return ${RETVAL}
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_GreaterThan()
 | |
|     # Description : Test case for checking if value is greater than something
 | |
|     # Returns     : (0 - SUCCESS;  1 - FAIL)
 | |
|     ################################################################################
 | |
|     TestCase_GreaterThan()
 | |
|     {
 | |
|         local RETVAL=1
 | |
|         if [ "$#" -ne "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to ${FUNCNAME}"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
|             LogText "${FUNCNAME}: checking if ${1} is greater than ${2}"
 | |
|             if [ "$1" > "$2" ]; then
 | |
|               LogText "${FUNCNAME}: ${1} is greater than ${2}"
 | |
|               RETVAL=0
 | |
|             else
 | |
|               LogText "${FUNCNAME}: ${1} is not greater than ${2}"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         return ${RETVAL}
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_GreaterOrEqual()
 | |
|     # Description : Test case for checking if value is greater than something
 | |
|     # Returns     : (0 - SUCCESS;  1 - FAIL)
 | |
|     ################################################################################
 | |
|     TestCase_GreaterOrEqual()
 | |
|     {
 | |
|         local RETVAL=1
 | |
|         if [ "$#" -ne "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to ${FUNCNAME}"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
|             LogText "${FUNCNAME}: checking if ${1} is greater or equal ${2}"
 | |
|             if [ TestCase_Equal "${1}" "${2}"  ] || [ TestCase_GreaterThan "${1}" "${2}" ]; then
 | |
|               RETVAL=0
 | |
|             fi
 | |
|         fi
 | |
|         return ${RETVAL}
 | |
|     }
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_LessThan()
 | |
|     # Description : Test case for checking if value is greater than something
 | |
|     # Returns     : (0 - SUCCESS;  1 - FAIL)
 | |
|     ################################################################################
 | |
|     TestCase_LessThan()
 | |
|     {
 | |
|         local RETVAL=1
 | |
|         if [ "$#" -ne "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to TestCase_GreaterOrEqual"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
|             LogText "${FUNCNAME}: checking if ${1} is less than ${2}"
 | |
|             if ! [ TestCase_GreaterOrEqual "${1}" "${2}"  ]; then
 | |
|               LogText "${FUNCNAME}:  ${1} is less than ${2}"
 | |
|               RETVAL=0
 | |
|             fi
 | |
|         fi
 | |
|         return ${RETVAL}
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestCase_LessOrEqual()
 | |
|     # Description : Test case for checking if value is less or equal something
 | |
|     # Returns     : (0 - SUCCESS;  1 - FAIL)
 | |
|     ################################################################################
 | |
|     TestCase_LessOrEqual()
 | |
|     {
 | |
|         local RETVAL=1
 | |
|         if [ "$#" -ne "2" ]; then
 | |
|             ReportException "${TEST_NO}" "Error in function call to ${FUNCNAME}"
 | |
|         else
 | |
|             LogText "${FUNCNAME}: checking value for application ${APP}"
 | |
|             LogText "${FUNCNAME}: ${OPTION} is set to ${1}"
 | |
| 
 | |
|             LogText "${FUNCNAME}: checking if ${1} is less or equal ${2}"
 | |
|             if [ TestCase_Equal "${1}" "${2}" ] || [ TestCase_LessThan "${1}" "${2}" ]; then
 | |
|               LogText "${FUNCNAME}:  ${1} is less than ${2}"
 | |
|               RETVAL=0
 | |
|             fi
 | |
|         fi
 | |
|         return ${RETVAL}
 | |
|     }
 | |
| 
 | |
|     # Check updates
 | |
|     CheckUpdates()
 | |
|       {
 | |
|         PROGRAM_LV="0000000000"; DB_MALWARE_LV="0000000000"; DB_FILEPERMS_LV="0000000000"
 | |
|         LYNIS_LV_RECORD="lynis-latest-version.cisofy.com."
 | |
|         FIND=`which dig 2> /dev/null`
 | |
|         if [ ! "${FIND}" = "" ]; then
 | |
|             PROGRAM_LV=`dig +short +time=3 -t txt lynis-latest-version.cisofy.com 2> /dev/null | grep -v "connection timed out" | sed 's/[".]//g' | grep "^[1-9][0-9][0-9]$"`
 | |
|           else
 | |
|             FIND=`which host 2> /dev/null`
 | |
|             if [ ! "${FIND}" = "" ]; then
 | |
|                 PROGRAM_LV=`host -t txt -W 3 lynis-latest-version.cisofy.com 2> /dev/null | grep -v "connection timed out" | awk '{ if ($1=="lynis-latest-version.cisofy.com" && $3=="text") { print $4 }}' | sed 's/"//g' | grep "^[1-9][0-9][0-9]$"`
 | |
|                 if [ "${PROGRAM_LV}" = "" ]; then PROGRAM_LV=0; fi
 | |
|               else
 | |
|                 FIND=`which drill 2> /dev/null`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     PROGRAM_LV=`drill txt ${LYNIS_LV_RECORD} | awk '{ if ($1=="lynis-latest-version.cisofy.com." && $4=="TXT") { print $5 }}' | tr -d '"' | grep "^[1-9][0-9][0-9]$"`
 | |
|                     if [ "${PROGRAM_LV}" = "" ]; then PROGRAM_LV=0; fi
 | |
|                   else
 | |
|                     LogText "Result: dig, drill or host not installed, update check skipped"
 | |
|                     UPDATE_CHECK_SKIPPED=1
 | |
|                 fi
 | |
|             fi
 | |
|          fi
 | |
|       }
 | |
| 
 | |
|     # Count the number of performed tests
 | |
|     counttests()
 | |
|       {
 | |
|         CTESTS_PERFORMED=`expr ${CTESTS_PERFORMED} + 1`
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : CreateTempFile
 | |
|     # Description : Creates a temporary file
 | |
|     # Returns     : TEMP_FILE
 | |
|     ################################################################################
 | |
| 
 | |
|     CreateTempFile()
 | |
|       {
 | |
|         TEMP_FILE=""
 | |
|         if [ "${OS}" = "AIX" ]; then
 | |
|             RANDOMSTRING1=`echo lynis-$(od -N4 -tu /dev/random | awk 'NR==1 {print $2} {}')`
 | |
|             TEMP_FILE="/tmp/${RANDOMSTRING1}"
 | |
|             touch ${TEMP_FILE}
 | |
|           else
 | |
|             TEMP_FILE=`mktemp /tmp/lynis.XXXXXXXXXX` || exit 1
 | |
|         fi
 | |
|         if [ ! "${TEMP_FILE}" = "" ]; then
 | |
|             logtext "Action: created temporary file ${TEMP_FILE}"
 | |
|           else
 | |
|             Fatal "Could not create a temporary file"
 | |
|         fi
 | |
|         # Add temporary file to queue for cleanup later
 | |
|         TEMP_FILES="${TEMP_FILES} ${TEMP_FILE}"
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Determine if a directory exists
 | |
|     DirectoryExists()
 | |
|       {
 | |
|         DIRECTORY_FOUND=0
 | |
|         LogText "Test: checking if directory $1 exists"
 | |
|         if [ -d $1 ]; then
 | |
|             LogText "Result: directory $1 exists"
 | |
|             DIRECTORY_FOUND=1
 | |
|           else
 | |
|             LogText "Result: directory $1 NOT found"
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Debug
 | |
|     # Description : Show additional information on screen
 | |
|     # Returns     : Nothing
 | |
|     ################################################################################
 | |
| 
 | |
|     Debug()
 | |
|       {
 | |
|         if [ ${DEBUG} -eq 1 ]; then echo "${PURPLE}[DEBUG]${NORMAL} $1"; fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : DigitsOnly
 | |
|     # Description : Only extract numbers from a string
 | |
|     # Returns     : Digits only string
 | |
|     ################################################################################
 | |
| 
 | |
|     DigitsOnly()
 | |
|       {
 | |
|         VALUE=$1
 | |
|         LogText "Value is now: ${VALUE}"
 | |
|         if [ ! "${AWKBINARY}" = "" ]; then
 | |
|             VALUE=`echo ${VALUE} | grep -Eo '[0-9]{1,}'`
 | |
|         fi
 | |
|         LogText "Returning value: ${VALUE}"
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Display
 | |
|     # Description : Show text on screen, with markup
 | |
|     # Returns     : Nothing
 | |
|     ################################################################################
 | |
| 
 | |
|     Display()
 | |
|       {
 | |
|         INDENT=0; TEXT=""; RESULT=""; COLOR=""; SPACES=0; SHOWDEBUG=0
 | |
|         while [ $# -ge 1 ]; do
 | |
|             case $1 in
 | |
|                 --color)
 | |
|                     shift
 | |
|                         case $1 in
 | |
|                           GREEN)   COLOR=$GREEN   ;;
 | |
|                           RED)     COLOR=$RED     ;;
 | |
|                           WHITE)   COLOR=$WHITE   ;;
 | |
|                           YELLOW)  COLOR=$YELLOW  ;;
 | |
|                         esac
 | |
|                 ;;
 | |
|                 --debug)
 | |
|                     SHOWDEBUG=1
 | |
|                 ;;
 | |
|                 --indent)
 | |
|                     shift
 | |
|                     INDENT=$1
 | |
|                 ;;
 | |
|                 --result)
 | |
|                     shift
 | |
|                     RESULT=$1
 | |
|                 ;;
 | |
|                 --text)
 | |
|                     shift
 | |
|                     TEXT=$1
 | |
|                 ;;
 | |
|                 *)
 | |
|                     echo "INVALID OPTION (Display): $1"
 | |
|                     ExitFatal
 | |
|                 ;;
 | |
|             esac
 | |
|             # Go to next parameter
 | |
|             shift
 | |
|         done
 | |
| 
 | |
|         if [ "${RESULT}" = "" ]; then
 | |
|             RESULTPART=""
 | |
|           else
 | |
|             if [ ${CRONJOB} -eq 0 ]; then
 | |
|                 RESULTPART=" [ ${COLOR}${RESULT}${NORMAL} ]"
 | |
|               else
 | |
|                 RESULTPART=" [ ${RESULT} ]"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         if [ ! "${TEXT}" = "" ]; then
 | |
|             # Show warnings always, and other messages if no quiet is being used
 | |
|             if [ ${QUIET} -eq 0 -o "${RESULT}" = "WARNING" ]; then
 | |
|                 # Display (counting with -m instead of -c, to support language locale)
 | |
|                 LINESIZE=`echo "${TEXT}" | wc -m | tr -d ' '`
 | |
|                 if [ ${SHOWDEBUG} -eq 1 ]; then DEBUGTEXT=" [${PURPLE}DEBUG${NORMAL}]"; else DEBUGTEXT=""; fi
 | |
|                 if [ ${INDENT} -gt 0 ]; then SPACES=`expr 62 - ${INDENT} - ${LINESIZE}`; fi
 | |
|                 if [ ${CRONJOB} -eq 0 ]; then
 | |
|                     # Check if we already have already discovered a proper echo command tool. It not, set it default to 'echo'.
 | |
|                     if [ "${ECHOCMD}" = "" ]; then ECHOCMD="echo"; fi
 | |
|                     ${ECHOCMD} "\033[${INDENT}C${TEXT}\033[${SPACES}C${RESULTPART}${DEBUGTEXT}"
 | |
|                   else
 | |
|                     echo "${TEXT}${RESULTPART}"
 | |
|                 fi
 | |
|             fi
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : DisplayManual
 | |
|     # Description : Show text on screen, without any markup
 | |
|     # Returns     : Nothing
 | |
|     ################################################################################
 | |
| 
 | |
|     DisplayManual()
 | |
|       {
 | |
|             if [ ${QUIET} -eq 0 ]; then
 | |
|                 ${ECHOCMD} "$1"
 | |
|             fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Clean exit (removing temp files, PID files)
 | |
|     ExitClean()
 | |
|       {
 | |
|         RemovePIDFile
 | |
|         RemoveTempFiles
 | |
|         LogText "${PROGRAM_NAME} ended successfully."
 | |
|         exit 0
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Clean exit with custom code
 | |
|     ExitCustom()
 | |
|       {
 | |
|         RemovePIDFile
 | |
|         RemoveTempFiles
 | |
|         # Exit with the exit code given, otherwise use 1
 | |
|         if [ $# -eq 1 ]; then
 | |
|             LogText "${PROGRAM_NAME} ended with exit code $1."
 | |
|             exit $1
 | |
|           else
 | |
|             LogText "${PROGRAM_NAME} ended with exit code 1."
 | |
|             exit 1
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Clean exit (removing temp files, PID files), with error code 1
 | |
|     ExitFatal()
 | |
|       {
 | |
|         RemovePIDFile
 | |
|         RemoveTempFiles
 | |
|         LogText "${PROGRAM_NAME} ended with exit code 1."
 | |
|         exit 1
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Determine if a file exists
 | |
|     FileExists()
 | |
|       {
 | |
|         FILE_FOUND=0
 | |
|         LogText "Test: checking if file $1 exists"
 | |
|         if [ -f $1 ]; then
 | |
|             LogText "Result: file $1 exists"
 | |
|             FILE_FOUND=1
 | |
|           else
 | |
|             LogText "Result: file $1 NOT found"
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : FileIsEmpty
 | |
|     # Description : Check if a file is empty
 | |
|     # Returns     : EMPTY (0 or 1)
 | |
|     ################################################################################
 | |
| 
 | |
|     FileIsEmpty()
 | |
|       {
 | |
|         EMPTY=0
 | |
|         LogText "Test: checking if file $1 is empty"
 | |
|         if [ -z $1 ]; then
 | |
|             LogText "Result: file $1 is empty"
 | |
|             EMPTY=1
 | |
|           else
 | |
|             LogText "Result: file $1 is NOT empty"
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : FileIsReadable
 | |
|     # Description : Check if a file readable or directory is accessible
 | |
|     # Returns     : Return code (0 = readable, 1 = not readable)
 | |
|     # Usage       : if FileIsReadable /etc/shadow; then echo "File is readable"; fi
 | |
|     ################################################################################
 | |
| 
 | |
|     FileIsReadable()
 | |
|       {
 | |
|         sFILE=$1
 | |
|         CANREAD=0
 | |
|         LogText "Test: testing if we can access ${sFILE}"
 | |
| 
 | |
|         # Check for symlink
 | |
|         if [ -L ${sFILE} ]; then
 | |
|             ShowSymlinkPath ${sFILE}
 | |
|             if [ ! "${SYMLINK}" = "" ]; then sFILE="${SYMLINK}"; fi
 | |
|         fi
 | |
| 
 | |
|         # Only check the file if it isn't a symlink (after previous check)
 | |
|         if [ -L ${sFILE} ]; then
 | |
|             OTHERPERMS="-"
 | |
|             LogText "Result: unclear if we can read this file, as this is a symlink"
 | |
|             ReportException "FileIsReadable" "Can not determine symlink ${sFILE}"
 | |
|           elif [ -d ${sFILE} ]; then
 | |
|             OTHERPERMS=`ls -d -l ${sFILE} | cut -c 8`
 | |
|           elif [ -f ${sFILE} ]; then
 | |
|             OTHERPERMS=`ls -d -l ${sFILE} | cut -c 8`
 | |
|           else
 | |
|             OTHERPERMS="-"
 | |
|         fi
 | |
| 
 | |
|         # Also check if we are the actual owner of the file (use -d to get directory itself, if its a directory)
 | |
|         FILEOWNER=`ls -dln ${sFILE} | awk -F" " '{ print $3 }'`
 | |
|         if [ "${FILEOWNER}" = "${MYID}" ]; then
 | |
|             LogText "Result: file is owned by our current user ID (${MYID}), checking if it is readable"
 | |
|             if [ -L ${sFILE} ]; then
 | |
|                 LogText "Result: unclear if we can read this file, as this is a symlink"
 | |
|                 ReportException "FileIsReadable" "Can not determine symlink ${sFILE}"
 | |
|             elif [ -d ${sFILE} ]; then
 | |
|                 OTHERPERMS=`ls -d -l ${sFILE} | cut -c 2`
 | |
|             elif [ -f ${sFILE} ]; then
 | |
|                 OTHERPERMS=`ls -d -l ${sFILE} | cut -c 2`
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: file is not owned by current user ID (${MYID}), but UID ${FILEOWNER}"
 | |
|         fi
 | |
| 
 | |
|         # Check if we have the read bit
 | |
|         if [ "${OTHERPERMS}" = "r" ]; then
 | |
|             CANREAD=1
 | |
|             return 0
 | |
|             LogText "Result: file ${sFILE} is readable (or directory accessible)."
 | |
|           else
 | |
|             LogText "Result: file ${sFILE} is NOT readable (or directory accessible), symlink, or does not exist. (OTHERPERMS: ${OTHERPERMS})"
 | |
|             return 1
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     # Get Host ID
 | |
|     GetHostID()
 | |
|       {
 | |
|         HOSTID="-"
 | |
|         FIND=""
 | |
|         # Avoid some hashes (empty, only zeros)
 | |
|         BLACKLISTED_HASHES="6ef1338f520d075957424741d7ed35ab5966ae97 adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"
 | |
|         if [ ! "${SHA1SUMBINARY}" = "" -o ! "${OPENSSLBINARY}" = "" -o ! "${CSUMBINARY}" = "" ]; then
 | |
| 
 | |
|             case "${OS}" in
 | |
| 
 | |
|                 "AIX")
 | |
|                     # Common interfaces: en0 en1 en2, ent0 ent1 ent2
 | |
|                     FIND=`entstat en0 2>/dev/null | grep "Hardware Address" | awk -F ": " '{ print $2 }'`
 | |
|                     if [ "${FIND}" = "" ]; then
 | |
|                         FIND=`entstat ent0 2>/dev/null | grep "Hardware Address" | awk -F ": " '{ print $2 }'`
 | |
|                     fi
 | |
|                     if [ ! "${FIND}" = "" ]; then
 | |
|                         # We have a MAC address, now hashing it
 | |
|                         if [ ! "${SHA1SUMBINARY}" = "" ]; then
 | |
|                             HOSTID=`echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }'`
 | |
|                         elif [ ! "${CSUMBINARY}" = "" ]; then
 | |
|                             HOSTID=`echo ${FIND} | ${CSUMBINARY} -h SHA1 - | awk '{ print $1 }'`
 | |
|                         elif [ ! "${OPENSSLBINARY}" = "" ]; then
 | |
|                             HOSTID=`echo ${FIND} | ${OPENSSLBINARY} sha -sha1 | awk '{ print $2 }'`
 | |
|                         else
 | |
|                             ReportException "GetHostID" "No sha1, sha1sum, csum or openssl binary available on AIX"
 | |
|                         fi
 | |
|                       else
 | |
|                         ReportException "GetHostID" "No output from entstat on interfaces: en0, ent0"
 | |
|                     fi
 | |
| 
 | |
|                 ;;
 | |
| 
 | |
|                 "DragonFly" | "FreeBSD")
 | |
|                      FIND=`${IFCONFIGBINARY} | grep ether | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                      if [ ! "${FIND}" = "" ]; then
 | |
|                          HOSTID=`echo ${FIND} | sha1`
 | |
|                        else
 | |
|                          ReportException "GetHostID" "No MAC address returned on DragonFly or FreeBSD"
 | |
|                      fi
 | |
|                 ;;
 | |
| 
 | |
|                 "Linux")
 | |
|                         # Define preferred interfaces
 | |
|                         #PREFERRED_INTERFACES="eth0 eth1 eth2 enp0s25"
 | |
| 
 | |
|                         # Only use ifconfig if no ip binary has been found
 | |
|                         if [ ! "${IFCONFIGBINARY}" = "" ]; then
 | |
|                             # Determine if we have ETH0 at all (not all Linux distro have this, e.g. Arch)
 | |
|                             HASETH0=`${IFCONFIGBINARY} | grep "^eth0"`
 | |
|                             # Check if we can find it with HWaddr on the line
 | |
|                             FIND=`${IFCONFIGBINARY} 2> /dev/null | grep "^eth0" | grep -v "eth0:" | grep HWaddr | awk '{ print $5 }' | tr '[:upper:]' '[:lower:]'`
 | |
| 
 | |
|                             # If nothing found, then try first for alternative interface. Else other versions of ifconfig (e.g. Slackware/Arch)
 | |
|                             if [ "${FIND}" = "" ]; then
 | |
|                                 FIND=`${IFCONFIGBINARY} 2> /dev/null | grep HWaddr`
 | |
|                                 if [ "${FIND}" = "" ]; then
 | |
|                                     # If possible directly address eth0 to avoid risking gathering the incorrect MAC address.
 | |
|                                     # If not, then falling back to getting first interface. Better than nothing.
 | |
|                                     if [ ! "${HASETH0}" = "" ]; then
 | |
|                                         FIND=`${IFCONFIGBINARY} eth0 2> /dev/null | grep "ether " | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                                       else
 | |
|                                         FIND=`${IFCONFIGBINARY} 2> /dev/null | grep "ether " | awk '{ print $2 }' | head -1 | tr '[:upper:]' '[:lower:]'`
 | |
|                                         if [ "${FIND}" = "" ]; then
 | |
|                                             ReportException "GetHostID" "No eth0 found (and no ether was found with ifconfig)"
 | |
|                                           else
 | |
|                                             LogText "Result: No eth0 found (ether found), using first network interface to determine hostid (with ifconfig)"
 | |
|                                         fi
 | |
|                                     fi
 | |
|                                   else
 | |
|                                     FIND=`${IFCONFIGBINARY} 2> /dev/null | grep HWaddr | head -1 | awk '{ print $5 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                                     LogText "GetHostID: No eth0 found (but HWaddr was found), using first network interface to determine hostid, with ifconfig"
 | |
|                                 fi
 | |
|                             fi
 | |
|                           else
 | |
|                             # See if we can use ip binary instead
 | |
|                             if [ ! "${IPBINARY}" = "" ]; then
 | |
|                                 # Determine if we have the common available eth0 interface
 | |
|                                 FIND=`${IPBINARY} addr show eth0 2> /dev/null | egrep "link/ether " | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                                 if [ "${FIND}" = "" ]; then
 | |
|                                     # Determine the MAC address of first interface with the ip command
 | |
|                                     FIND=`${IPBINARY} addr show 2> /dev/null | egrep "link/ether " | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                                     if [ "${FIND}" = "" ]; then
 | |
|                                         ReportException "GetHostID" "Can't create hostid (no MAC addresses found)"
 | |
|                                     fi
 | |
|                                 fi
 | |
|                               else
 | |
|                                 ReportException "GetHostID" "Can't create hostid, missing both ifconfig and ip binary"
 | |
|                             fi
 | |
|                         fi
 | |
| 
 | |
|                         # Check if we found a HostID
 | |
|                         if [ ! "${FIND}" = "" ]; then
 | |
|                             HOSTID=`echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }'`
 | |
|                             LogText "Result: Found HostID: ${HOSTID}"
 | |
|                           else
 | |
|                             ReportException "GetHostID" "Can't create HOSTID, command ip not found"
 | |
|                         fi
 | |
|                 ;;
 | |
| 
 | |
|                 "MacOS")
 | |
|                      FIND=`${IFCONFIGBINARY} en0 | grep ether | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                      if [ ! "${FIND}" = "" ]; then
 | |
|                          HOSTID=`echo ${FIND} | shasum | awk '{ print $1 }'`
 | |
|                        else
 | |
|                          ReportException "GetHostID" "No MAC address returned on Mac OS"
 | |
|                      fi
 | |
|                 ;;
 | |
| 
 | |
|                 "NetBSD")
 | |
|                      FIND=`${IFCONFIGBINARY} -a | grep "address:" | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                      if [ ! "${FIND}" = "" ]; then
 | |
|                          HOSTID=`echo ${FIND} | sha1`
 | |
|                        else
 | |
|                          ReportException "GetHostID" "No MAC address returned on NetBSD"
 | |
|                      fi
 | |
|                 ;;
 | |
| 
 | |
|                 "OpenBSD")
 | |
|                      FIND=`${IFCONFIGBINARY} | grep "lladdr " | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]'`
 | |
|                      if [ ! "${FIND}" = "" ]; then
 | |
|                          HOSTID=`echo ${FIND} | sha1`
 | |
|                        else
 | |
|                          ReportException "GetHostID" "No MAC address returned on OpenBSD"
 | |
|                      fi
 | |
|                 ;;
 | |
| 
 | |
|                 "Solaris")
 | |
|                     INTERFACES_TO_TEST="e1000g1 net0"
 | |
|                     FOUND=0
 | |
|                     for I in ${INTERFACES_TO_TEST}; do
 | |
|                          FIND=`${IFCONFIGBINARY} -a | grep "^${I}"`
 | |
|                          if [ ! "${FIND}" = "" ]; then
 | |
|                              FOUND=1; LogText "Found interface ${I} on Solaris"
 | |
|                          fi
 | |
|                     done
 | |
|                     if [ ${FOUND} -eq 1 ]; then
 | |
|                         FIND=`${IFCONFIGBINARY} ${I} | grep ether | awk '{ if ($1=="ether") { print $2 }}'`
 | |
|                         if [ ! "${SHA1SUMBINARY}" = "" ]; then
 | |
|                             HOSTID=`echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }'`
 | |
|                           else
 | |
|                             if [ ! "${OPENSSLBINARY}" = "" ]; then
 | |
|                                 HOSTID=`echo ${FIND} | ${OPENSSLBINARY} sha -sha1 | awk '{ print $2 }'`
 | |
|                               else
 | |
|                                 ReportException "GetHostID" "Can not find sha1/sha1sum or openssl"
 | |
|                             fi
 | |
|                         fi
 | |
|                       else
 | |
|                         ReportException "GetHostID" "No interface found op Solaris to create HostID"
 | |
|                     fi
 | |
|                 ;;
 | |
| 
 | |
| 
 | |
|                 *)
 | |
|                         ReportException "GetHostID" "Can't create HOSTID as OS is not supported yet by this function"
 | |
|                 ;;
 | |
|             esac
 | |
|             # Remove HOSTID if it contains a default MAC address with a related hash value
 | |
|             if [ ! "${HOSTID}" = "" ]; then
 | |
|                 for CHECKHASH in ${BLACKLISTED_HASHES}; do
 | |
|                     if [ "${CHECKHASH}" = "${HOSTID}" ]; then
 | |
|                         LogText "Result: hostid is a blacklisted value"
 | |
|                         HOSTID=""
 | |
|                     fi
 | |
|                 done
 | |
|             fi
 | |
|           else
 | |
|             ReportException "GetHostID" "Can't create HOSTID as there is no SHA1 hash tool available (sha1, sha1sum, openssl)"
 | |
|         fi
 | |
| 
 | |
|         # Search machine ID
 | |
|         # This applies to IDs generated for systemd
 | |
|         # Optional: DBUS creates ID as well with dbus-uuidgen and is stored in /var/lib/dbus-machine-id (might be symlinked to /etc/machine-id)
 | |
|         sMACHINEIDFILE="/etc/machine-id"
 | |
|         if [ -f ${sMACHINEIDFILE} ]; then
 | |
|             FIND=`head -1 ${sMACHINEIDFILE} | grep "^[a-f0-9]"`
 | |
|             if [ "${FIND}" = "" ]; then
 | |
|                 MACHINEID="${FIND}"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         if [ "${HOSTID}" = "" ]; then
 | |
|             LogText "Result: no HOSTID available, trying to use SSH key as unique source"
 | |
|             # Create host ID when a MAC address was not found
 | |
|             SSH_KEY_FILES="ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_dsa_key ssh_host_dsa_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub"
 | |
|             if [ -d /etc/ssh ]; then
 | |
|                 for I in ${SSH_KEY_FILES}; do
 | |
|                     if [ "${HOSTID}" = "" ]; then
 | |
|                         if [ -f /etc/ssh/${I} ]; then
 | |
|                             LogText "Result: found ${I} in /etc/ssh"
 | |
|                             if [ ! "${SHA1SUMBINARY}" = "" ]; then
 | |
|                                 HOSTID=`cat /etc/ssh/${I} | ${SHA1SUMBINARY} | awk '{ print $1 }'`
 | |
|                                 LogText "result: Created HostID with SSH key ($I): ${HOSTID}"
 | |
|                               else
 | |
|                                 ReportException "GetHostID" "Can't create HOSTID with SSH key, as sha1sum binary is missing"
 | |
|                             fi
 | |
|                         fi
 | |
|                     fi
 | |
|                 done
 | |
|               else
 | |
|                 LogText "Result: no /etc/ssh directory found, skipping"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         # Show an exception if no HostID could be created, to ensure each system (and scan) has one
 | |
|         if [ "${HOSTID}" = "" ]; then
 | |
|             ReportException "GetHostID" "No unique host identifier could be created."
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     # Insert section block
 | |
|     InsertSection()
 | |
|       {
 | |
|         if [ ${QUIET} -eq 0 ]; then
 | |
|             echo ""
 | |
|             echo "[+] ${SECTION}$1${NORMAL}"
 | |
|             echo "------------------------------------"
 | |
|         fi
 | |
|         logtextbreak
 | |
|         LogText "Action: Performing tests from category: $1"
 | |
|       }
 | |
| 
 | |
|     # Insert section block for plugins
 | |
|     InsertPluginSection()
 | |
|       {
 | |
|         if [ ${QUIET} -eq 0 ]; then
 | |
|             echo ""
 | |
|             echo "[+] ${MAGENTA}$1${NORMAL}"
 | |
|             echo "------------------------------------"
 | |
|         fi
 | |
|         LogText "Action: Performing plugin tests"
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : IsRunning()
 | |
|     # Description : Check if a process is running
 | |
|     # Returns     : 0 (process is running), 1 (process not running)
 | |
|     #               RUNNING (1 = running, 0 = not running)
 | |
|     ################################################################################
 | |
| 
 | |
|     IsRunning()
 | |
|       {
 | |
|         RUNNING=0
 | |
|         PSOPTIONS=""
 | |
|         if [ ${SHELL_IS_BUSYBOX} -eq 0 ]; then PSOPTIONS=" ax"; fi
 | |
|         FIND=`${PSBINARY} ${PSOPTIONS} | egrep "( |/)$1" | grep -v "grep"`
 | |
|         if [ ! "${FIND}" = "" ]; then
 | |
|             RUNNING=1
 | |
|             LogText "IsRunning: process '$1' found (${FIND})"
 | |
|             return 0
 | |
|           else
 | |
|             LogText "IsRunning: process '$1' not found"
 | |
|             return 1
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : IsVirtualMachine()
 | |
|     # Description : Check if a specific item exists in the report
 | |
|     # Returns     : ISVIRTUALMACHINE (0-2)
 | |
|     #               VMTYPE
 | |
|     #               VMFULLTYPE
 | |
|     ################################################################################
 | |
| 
 | |
|     IsVirtualMachine()
 | |
|       {
 | |
|         LogText "Test: Determine if this system is a virtual machine"
 | |
|         # 0 = no, 1 = yes, 2 = unknown
 | |
|         ISVIRTUALMACHINE=2; VMTYPE="unknown"; VMFULLTYPE="Unknown"
 | |
|         SHORT=""
 | |
| 
 | |
|         # facter
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             if [ -x /usr/bin/facter ]; then
 | |
|                 case "`facter is_virtual`" in
 | |
|                 "true")
 | |
|                     SHORT=`facter virtual`
 | |
|                     LogText "Result: found ${SHORT}"
 | |
|                 ;;
 | |
|                 "false")
 | |
|                     LogText "Result: facter says this machine is not a virtual"
 | |
|                 ;;
 | |
|                 esac
 | |
|               else
 | |
|                 LogText "Result: facter utility not found"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped facter test, as we already found machine type"
 | |
|         fi
 | |
| 
 | |
|         # systemd
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             if [ -x /usr/bin/systemd-detect-virt ]; then
 | |
|                 LogText "Test: trying to guess virtualization technology with systemd-detect-virt"
 | |
|                 FIND=`/usr/bin/systemd-detect-virt`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     LogText "Result: found ${FIND}"
 | |
|                     SHORT="${FIND}"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Result: systemd-detect-virt not found"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped systemd test, as we already found machine type"
 | |
|         fi
 | |
| 
 | |
|         # lscpu
 | |
|         # Values: VMware
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             if [ -x /usr/bin/lscpu ]; then
 | |
|                 LogText "Test: trying to guess virtualization with lscpu"
 | |
|                 FIND=`lscpu | grep "^Hypervisor Vendor" | awk -F: '{ print $2 }' | sed 's/ //g'`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     LogText "Result: found ${FIND}"
 | |
|                     SHORT="${FIND}"
 | |
|                   else
 | |
|                     LogText "Result: can't find hypervisor vendor with lscpu"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Result: lscpu not found"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped lscpu test, as we already found machine type"
 | |
|         fi
 | |
| 
 | |
|         # dmidecode
 | |
|         # Values: VMware Virtual Platform / VirtualBox
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             if [ -x /usr/bin/dmidecode ]; then DMIDECODE_BINARY="/usr/bin/dmidecode"
 | |
|               elif [ -x /usr/sbin/dmidecode ]; then DMIDECODE_BINARY="/usr/sbin/dmidecode"
 | |
|               else DMIDECODE_BINARY=""
 | |
|             fi
 | |
|             if [ ! "${DMIDECODE_BINARY}" = "" ]; then
 | |
|                 LogText "Test: trying to guess virtualization with dmidecode"
 | |
|                 FIND=`/usr/sbin/dmidecode -s system-product-name | awk '{ print $1 }'`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     LogText "Result: found ${FIND}"
 | |
|                     SHORT="${FIND}"
 | |
|                   else
 | |
|                     LogText "Result: can't find product name with dmidecode"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Result: dmidecode not found"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped dmidecode test, as we already found machine type"
 | |
|         fi
 | |
| 
 | |
|         # lshw
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             if [ -x /usr/bin/lshw ]; then
 | |
|                 LogText "Test: trying to guess virtualization with lshw"
 | |
|                 FIND=`lshw -quiet -class system | awk '{ if ($1=="product:") { print $2 }}'`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     LogText "Result: found ${FIND}"
 | |
|                     SHORT="${FIND}"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Result: lshw not found"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped lshw test, as we already found machine type"
 | |
|         fi
 | |
| 
 | |
|         # Other options
 | |
|         # SaltStack: salt-call grains.get virtual
 | |
|         # < needs snippet >
 | |
| 
 | |
|         # Try common guest processes
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             LogText "Test: trying to guess virtual machine type by running processes"
 | |
| 
 | |
|             # VMware
 | |
|                 IsRunning vmware-guestd
 | |
|                 if [ ${RUNNING} -eq 1 ]; then SHORT="vmware"; fi
 | |
|                 IsRunning vmtoolsd
 | |
|                 if [ ${RUNNING} -eq 1 ]; then SHORT="vmware"; fi
 | |
| 
 | |
|             # VirtualBox based on guest services
 | |
|                 IsRunning vboxguest-service
 | |
|                 if [ ${RUNNING} -eq 1 ]; then SHORT="virtualbox"; fi
 | |
|                 IsRunning VBoxClient
 | |
|                 if [ ${RUNNING} -eq 1 ]; then SHORT="virtualbox"; fi
 | |
|           else
 | |
|             LogText "Result: skipped processes test, as we already found platform"
 | |
|         fi
 | |
| 
 | |
|         # Amazon EC2
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             LogText "Test: checking specific files for Amazon"
 | |
|             if [ -f /etc/ec2_version -a ! -z /etc/ec2_version ]; then
 | |
|                 SHORT="amazon-ec2"
 | |
|               else
 | |
|                 LogText "Result: system not hosted on Amazon"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped Amazon EC2 test, as we already found platform"
 | |
|         fi
 | |
| 
 | |
|         # sysctl values
 | |
|         if [ "${SHORT}" = "" ]; then
 | |
|             LogText "Test: trying to guess virtual machine type by sysctl keys"
 | |
| 
 | |
|             # FreeBSD: hw.hv_vendor (remains empty for VirtualBox)
 | |
|             # NetBSD: machdep.dmi.system-product
 | |
|             # OpenBSD: hw.product
 | |
|             FIND=`sysctl -a 2> /dev/null | egrep "(hw.product|machdep.dmi.system-product)" | head -1 | sed 's/ = /=/' | awk -F= '{ print $2 }'`
 | |
|             if [ ! "${FIND}" = "" ]; then
 | |
|                 SHORT="${FIND}"
 | |
|             fi
 | |
|           else
 | |
|             LogText "Result: skipped sysctl test, as we already found platform"
 | |
|         fi
 | |
| 
 | |
|         # Check if we catched some string along all tests
 | |
|         if [ ! "${SHORT}" = "" ]; then
 | |
|             # Lowercase and see if we found a match
 | |
|             SHORT=`echo ${SHORT} | awk '{ print $1 }' | tr [[:upper:]] [[:lower:]]`
 | |
| 
 | |
|                 case ${SHORT} in
 | |
|                     amazon-ec2)         ISVIRTUALMACHINE=1; VMTYPE="amazon-ec2";      VMFULLTYPE="Amazon AWS EC2 Instance"                 ;;
 | |
|                     bochs)              ISVIRTUALMACHINE=1; VMTYPE="bochs";           VMFULLTYPE="Bochs CPU emulation"                     ;;
 | |
|                     docker)             ISVIRTUALMACHINE=1; VMTYPE="docker";          VMFULLTYPE="Docker container"                        ;;
 | |
|                     kvm)                ISVIRTUALMACHINE=1; VMTYPE="kvm";             VMFULLTYPE="KVM"                                     ;;
 | |
|                     lxc)                ISVIRTUALMACHINE=1; VMTYPE="lxc";             VMFULLTYPE="Linux Containers"                        ;;
 | |
|                     lxc-libvirt)        ISVIRTUALMACHINE=1; VMTYPE="lxc-libvirt";     VMFULLTYPE="libvirt LXC driver (Linux Containers)"   ;;
 | |
|                     microsoft)          ISVIRTUALMACHINE=1; VMTYPE="microsoft";       VMFULLTYPE="Microsoft Virtual PC"                    ;;
 | |
|                     openvz)             ISVIRTUALMACHINE=1; VMTYPE="openvz";          VMFULLTYPE="OpenVZ"                                  ;;
 | |
|                     oracle|virtualbox)  ISVIRTUALMACHINE=1; VMTYPE="virtualbox";      VMFULLTYPE="Oracle VM VirtualBox"                    ;;
 | |
|                     qemu)               ISVIRTUALMACHINE=1; VMTYPE="qemu";            VMFULLTYPE="QEMU"                                    ;;
 | |
|                     systemd-nspawn)     ISVIRTUALMACHINE=1; VMTYPE="systemd-nspawn";  VMFULLTYPE="Systemd Namespace container"             ;;
 | |
|                     uml)                ISVIRTUALMACHINE=1; VMTYPE="uml";             VMFULLTYPE="User-Mode Linux (UML)"                   ;;
 | |
|                     vmware)             ISVIRTUALMACHINE=1; VMTYPE="vmware";          VMFULLTYPE="VMware product"                          ;;
 | |
|                     xen)                ISVIRTUALMACHINE=1; VMTYPE="xen";             VMFULLTYPE="XEN"                                     ;;
 | |
|                     zvm)                ISVIRTUALMACHINE=1; VMTYPE="zvm";             VMFULLTYPE="IBM z/VM"                                ;;
 | |
|                     *)                  LogText "Result: Unknown virtualization type, so most likely system is physical"                   ;;
 | |
|                 esac
 | |
|         fi
 | |
| 
 | |
|         # Check final status
 | |
|         if [ ${ISVIRTUALMACHINE} -eq 1 ]; then
 | |
|             LogText "Result: found virtual machine (type: ${VMTYPE}, ${VMFULLTYPE})"
 | |
|             Report "vm=1"
 | |
|             Report "vmtype=${VMTYPE}"
 | |
|         elif [ ${ISVIRTUALMACHINE} -eq 2 ]; then
 | |
|             LogText "Result: unknown if this system is a virtual machine"
 | |
|             Report "vm=2"
 | |
|           else
 | |
|             LogText "Result: system seems to be non-virtual"
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     # Function IsWorldReadable
 | |
|     IsWorldReadable()
 | |
|       {
 | |
|         sFILE=$1
 | |
|         # Check for symlink
 | |
|         if [ -L ${sFILE} ]; then
 | |
|             ShowSymlinkPath ${sFILE}
 | |
|             if [ ! "${SYMLINK}" = "" ]; then
 | |
|                 sFILE="${SYMLINK}"
 | |
|             fi
 | |
|         fi
 | |
|         # Only check the file if it isn't a symlink (after previous check)
 | |
|         if [ -f ${sFILE} -a ! -L ${sFILE} ]; then
 | |
|             FINDVAL=`ls -l ${sFILE} | cut -c 8`
 | |
|             if [ "${FINDVAL}" = "r" ]; then return 0; else return 1; fi
 | |
|           else
 | |
|             return 255
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Function IsWorldExecutable
 | |
|     IsWorldExecutable()
 | |
|       {
 | |
|         sFILE=$1
 | |
|         # Check for symlink
 | |
|         if [ -L ${sFILE} ]; then
 | |
|             ShowSymlinkPath ${sFILE}
 | |
|             if [ ! "${SYMLINK}" = "" ]; then
 | |
|                 sFILE="${SYMLINK}"
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         # Only check the file if it isn't a symlink (after previous check)
 | |
|         if [ -f ${sFILE} -a ! -L ${sFILE} ]; then
 | |
|             FINDVAL=`ls -l ${sFILE} | cut -c 10`
 | |
|             if [ "${FINDVAL}" = "x" ]; then return 0; else return 1; fi
 | |
|           else
 | |
|             return 255
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : IsWorldWritable()
 | |
|     # Description : Determines if a file is writable for all users
 | |
|     # Returns     : exit code (0 = writable, 1 = not writable, 255 = error)
 | |
|     # Usage       : if IsWorldWritable /etc/motd; then echo "File is writable"; fi
 | |
|     ################################################################################
 | |
| 
 | |
|     IsWorldWritable()
 | |
|       {
 | |
|         sFILE=$1
 | |
|         FileIsWorldWritable=""
 | |
| 
 | |
|         # Only check the file if it isn't a symlink (after previous check)
 | |
|         if [ -f ${sFILE} -a ! -L ${sFILE} ]; then
 | |
|             FINDVAL=`ls -l ${sFILE} | cut -c 9`
 | |
|             if [ "${FINDVAL}" = "w" ]; then return 0; else return 1; fi
 | |
|           else
 | |
|             return 255
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : LogText()
 | |
|     # Description : Function logtext (redirect data ($1) to log file)
 | |
|     # Returns     : Nothing
 | |
|     # Usage       : LogText "This goes into the log file"
 | |
|     ################################################################################
 | |
| 
 | |
|     LogText()
 | |
|       {
 | |
|         if [ ! "${LOGFILE}" = "" ]; then
 | |
|             CDATE=`date "+[%H:%M:%S]"`
 | |
|             echo "${CDATE} $1" >> ${LOGFILE}
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     # Alias for older tests (do no longer use this as it will be deprecated)
 | |
|     logtext()
 | |
|       {
 | |
|           LogText "$1"
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : logtextbreak()
 | |
|     # Description : Add a separator to log file between sections, tests etc
 | |
|     # Returns     : <nothing>
 | |
|     logtextbreak()
 | |
|       {
 | |
|         if [ ! "${LOGFILE}" = "" ]; then
 | |
|             CDATE=`date "+[%H:%M:%S]"`
 | |
|             echo "${CDATE} ===---------------------------------------------------------------===" >> ${LOGFILE}
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Maid()
 | |
|     # Description : Cleanup service
 | |
|     # Returns     : <nothing>
 | |
|     Maid()
 | |
|       {
 | |
|         echo ""; echo "Interrupt detected."
 | |
|         # Remove PID
 | |
|         RemovePIDFile
 | |
|         RemoveTempFiles
 | |
| 
 | |
|         Display --text "Cleaning up..." --result DONE --color GREEN
 | |
| 
 | |
|         ExitFatal
 | |
|       }
 | |
| 
 | |
|     # Parse nginx configuration lines
 | |
|     ParseNginx()
 | |
|       {
 | |
|         FIND=`awk -F= '/^nginx_config_option=/ { print $2 }' ${REPORTFILE} | sed 's/ /:space:/g'`
 | |
|         for I in ${FIND}; do
 | |
|             I=`echo ${I} | sed 's/:space:/ /g' | sed 's/;$//'`
 | |
|             OPTION=`echo ${I} | awk '{ print $1 }'`
 | |
|             VALUE=`echo ${I}| cut -d' ' -f2-`
 | |
|             LogText "Result: found option ${OPTION} with parameters ${VALUE}"
 | |
|             case ${OPTION} in
 | |
|                 access_log)
 | |
|                     if [ "${VALUE}" = "off" ]; then
 | |
|                         LogText "Result: found logging disabled for one virtual host"
 | |
|                         NGINX_ACCESS_LOG_DISABLED=1
 | |
|                       else
 | |
|                         if [ ! "${VALUE}" = "" ]; then
 | |
|                             # If multiple values follow, select first one
 | |
|                             VALUE=`echo ${VALUE} | awk '{ print $1 }'`
 | |
|                             if [ ! -f ${VALUE} ]; then
 | |
|                                 LogText "Result: could not find referenced log file ${VALUE} in nginx configuration"
 | |
|                                 NGINX_ACCESS_LOG_MISSING=1
 | |
|                             fi
 | |
|                         fi
 | |
|                     fi
 | |
|                 ;;
 | |
|                 # Headers
 | |
|                 add_header)
 | |
|                     HEADER=`echo ${VALUE} | awk '{ print $1 }'`
 | |
|                     HEADER_VALUE=`echo ${VALUE} | cut -d' ' -f2-`
 | |
|                     LogText "Result: found header ${HEADER} with value ${HEADER_VALUE}"
 | |
|                     #Report "nginx_header[]=${HEADER}|${HEADER_VALUE}|"
 | |
|                 ;;
 | |
|                 alias)
 | |
|                     NGINX_ALIAS_FOUND=1
 | |
|                 ;;
 | |
|                 allow)
 | |
|                     NGINX_ALLOW_FOUND=1
 | |
|                 ;;
 | |
|                 autoindex)
 | |
|                 ;;
 | |
|                 deny)
 | |
|                     NGINX_DENY_FOUND=1
 | |
|                 ;;
 | |
|                 expires)
 | |
|                     NGINX_EXPIRES_FOUND=1
 | |
|                 ;;
 | |
|                 error_log)
 | |
|                     # Check if debug is appended
 | |
|                     FIND=`echo ${VALUE} | awk '{ if ($2=="debug") { print 1 } else { print 0 }}'`
 | |
|                     if [ ${FIND} -eq 1 ]; then
 | |
|                         NGINX_ERROR_LOG_DEBUG=1
 | |
|                     fi
 | |
|                     # Check if log file exists
 | |
|                     FILE=`echo ${VALUE} | awk '{ print $1 }'`
 | |
|                     if [ ! "${FILE}" = "" ]; then
 | |
|                         if [ ! -f ${FILE} ]; then
 | |
|                           NGINX_ERROR_LOG_MISSING=1
 | |
|                         fi
 | |
|                       else
 | |
|                         LogText "Warning: did not find a filename after error_log in nginx configuration"
 | |
|                     fi
 | |
|                 ;;
 | |
|                 error_page)
 | |
|                 ;;
 | |
|                 fastcgi_intercept_errors)
 | |
|                 ;;
 | |
|                 fastcgi_param)
 | |
|                     NGINX_FASTCGI_FOUND=1
 | |
|                     NGINX_FASTCGI_PARAMS_FOUND=1
 | |
|                 ;;
 | |
|                 fastcgi_pass)
 | |
|                     NGINX_FASTCGI_FOUND=1
 | |
|                     NGINX_FASTCGI_PASS_FOUND=1
 | |
|                 ;;
 | |
|                 fastcgi_pass_header)
 | |
|                 ;;
 | |
|                 index)
 | |
|                 ;;
 | |
|                 keepalive_timeout)
 | |
|                 ;;
 | |
|                 listen)
 | |
|                     NGINX_LISTEN_FOUND=1
 | |
|                     # Test for ssl on listen statement
 | |
|                     FIND_SSL=`echo ${VALUE} | grep ssl`
 | |
|                     if [ ! "${FIND_SSL}" = "" ]; then NGINX_SSL_ON=1; fi
 | |
|                 ;;
 | |
|                 location)
 | |
|                     NGINX_LOCATION_FOUND=1
 | |
|                 ;;
 | |
|                 return)
 | |
|                     NGINX_RETURN_FOUND=1
 | |
|                 ;;
 | |
|                 root)
 | |
|                     NGINX_ROOT_FOUND=1
 | |
|                 ;;
 | |
|                 server_name)
 | |
|                 ;;
 | |
|                 ssl)
 | |
|                     if [ "${VALUE}" = "on" ]; then NGINX_SSL_ON=1; fi
 | |
|                 ;;
 | |
|                 ssl_certificate)
 | |
|                     LogText "Found SSL certificate in nginx configuration"
 | |
|                 ;;
 | |
|                 ssl_certificate_key)
 | |
|                 ;;
 | |
|                 ssl_ciphers)
 | |
|                     NGINX_SSL_CIPHERS=1
 | |
|                 ;;
 | |
|                 ssl_prefer_server_ciphers)
 | |
|                     if [ "${VALUE}" = "on" ]; then NGINX_SSL_PREFER_SERVER_CIPHERS=1; fi
 | |
|                 ;;
 | |
|                 ssl_protocols)
 | |
|                     NGINX_SSL_PROTOCOLS=1
 | |
|                     #Report "nginx_ssl_protocols=${VALUE}"
 | |
|                 ;;
 | |
|                 ssl_session_cache)
 | |
|                 ;;
 | |
|                 ssl_session_timeout)
 | |
|                 ;;
 | |
|                 types)
 | |
|                 ;;
 | |
|                 *)
 | |
|                     LogText "Found unknown option ${OPTION} in nginx configuration"
 | |
|                 ;;
 | |
|             esac
 | |
|         done
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : PortIsListening()
 | |
|     # Description : Check if machine is listening on specified protocol and port
 | |
|     # Returns     : exit code 0 (listening) or 1 (not listening)
 | |
|     # Usage       : if PortIsListening "TCP" 22; then echo "Port is listening"; fi
 | |
|     ################################################################################
 | |
| 
 | |
|     PortIsListening()
 | |
|       {
 | |
|         if [ "${LSOFBINARY}" = "" ]; then
 | |
|             return 255
 | |
|           else
 | |
|             if [ $# -eq 2 ] && [ $1 = "TCP" -o $1 = "UDP" ]; then
 | |
|                 LogText "Test: find service listening on $1:$2"
 | |
|                 if [ $1 = "TCP" ]; then FIND=`${LSOFBINARY} -i${1} -s${1}:LISTEN -P -n | grep ":${2} "`; else FIND=`${LSOFBINARY} -i${1} -P -n | grep ":${2} "`; fi
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     LogText "Result: found service listening on port $2 ($1)"
 | |
|                     return 0
 | |
|                   else
 | |
|                     LogText "Result: did not find service listening on port $2 ($1)"
 | |
|                     return 1
 | |
|                 fi
 | |
|               else
 | |
|                 return 255
 | |
|                 ReportException ${TEST_NO} "Error in function call to PortIsListening"
 | |
|             fi
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Progress()
 | |
|     # Description : Displays progress on screen with dots
 | |
|     # Input       : finish or text
 | |
|     # Returns     : nothing
 | |
|     # Tip         : Use this function from Register with the --progress parameter
 | |
|     ################################################################################
 | |
| 
 | |
|     Progress()
 | |
|       {
 | |
|         if [ ${CRONJOB} -eq 0 ]; then
 | |
|             if [ "$1" = "--finish" ]; then
 | |
|                 ${ECHOCMD} ""
 | |
|             else
 | |
|                 # If the No-Break version of echo is known, use that (usually breaks in combination with -e)
 | |
|                 if [ ! "${ECHONB}" = "" ]; then
 | |
|                     ${ECHONB} "$1"
 | |
|                   else
 | |
|                     ${ECHOCMD} -en "$1"
 | |
|                 fi
 | |
|             fi
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Progress()
 | |
|     # Description : Displays progress on screen with dots
 | |
|     # Input       : Amount of characters (optional)
 | |
|     # Returns     : RANDOMSTRING
 | |
|     # Usage       : RandomString 32
 | |
|     ################################################################################
 | |
| 
 | |
|     RandomString() {
 | |
|         # Check a (pseudo) random character device
 | |
|         if [ -c /dev/urandom ]; then local FILE="/dev/urandom"
 | |
|         elif [ -c /dev/random ]; then local FILE="/dev/random"
 | |
|         else
 | |
|           Display "Can not use RandomString function, as there is no random device to be used"
 | |
|         fi
 | |
|         if [ $# -eq 0 ]; then local SIZE=16; else local SIZE=$1; fi
 | |
|         local CSIZE=`expr ${SIZE} / 2`
 | |
|         RANDOMSTRING=`head -c ${CSIZE} /dev/urandom | od -An -x | tr -d ' ' | cut -c 1-${SIZE}`
 | |
|     }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : RealFilename()
 | |
|     # Description : Return file behind a symlink
 | |
|     # Returns     : sFILE
 | |
|     # Notes       : This function is unused, use ShowSymlinkPath instead
 | |
|     #RealFilename()
 | |
|     #  {
 | |
|     #    sFILE=$1
 | |
|     #    FileIsWorldExecutable=""
 | |
|     #    SYMLINK=0
 | |
|     #
 | |
|     #    # Check for symlink
 | |
|     #    if [ -L ${sFILE} ]; then
 | |
|     #        if [ ! "${READLINKBINARY}" = "" ]; then
 | |
|     #            tFILE=`${READLINKBINARY} -f ${sFILE}`
 | |
|     #            # Check if we can find the file now
 | |
|     #            if [ -f ${tFILE} ]; then
 | |
|     #                rFILE="${tFILE}"
 | |
|     #                LogText "Result: symlink found, pointing to ${sFILE}"
 | |
|     #                SYMLINK=1
 | |
|     #              else
 | |
|     #                # Check the full path of the symlink, strip the filename, copy the path and linked filename together
 | |
|     #                tDIR=`echo ${sFILE} | awk '{match($1, "^.*/"); print substr($1, 1, RLENGTH-1)}'`
 | |
|     #                tFILE="${tDIR}/${tFILE}"
 | |
|     #                if [ -f ${tFILE} ]; then
 | |
|     #                  rFILE="${tFILE}"
 | |
|     #                  LogText "Result: symlink found, seems to be ${rFILE}"
 | |
|     #                fi
 | |
|     #            fi
 | |
|     #        fi
 | |
|     #      else
 | |
|     #        # No symlink
 | |
|     #        rFILE="${sFILE}"
 | |
|     #    fi
 | |
|     #  }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : Register()
 | |
|     # Description : Register a test and see if it has to be run
 | |
|     # Returns     : SKIPTEST (0 or 1)
 | |
|     Register()
 | |
|       {
 | |
|         # Do not insert a log break, if previous test was not logged
 | |
|         if [ ${SKIPLOGTEST} -eq 0 ]; then logtextbreak; fi
 | |
|         ROOT_ONLY=0; SKIPTEST=0; SKIPLOGTEST=0; TEST_NEED_OS=""; PREQS_MET=""
 | |
|         TEST_NEED_NETWORK=""; TEST_NEED_PLATFORM=""
 | |
|         TOTAL_TESTS=`expr ${TOTAL_TESTS} + 1`
 | |
|         while [ $# -ge 1 ]; do
 | |
|             case $1 in
 | |
|                 --description)
 | |
|                     shift
 | |
|                     TEST_DESCRIPTION=$1
 | |
|                 ;;
 | |
|                 --platform)
 | |
|                     shift
 | |
|                     TEST_NEED_PLATFORM=$1
 | |
|                 ;;
 | |
|                 --network)
 | |
|                     shift
 | |
|                     TEST_NEED_NETWORK=$1
 | |
|                 ;;
 | |
|                 --os)
 | |
|                     shift
 | |
|                     TEST_NEED_OS=$1
 | |
|                 ;;
 | |
|                 --preqs-met)
 | |
|                     shift
 | |
|                     PREQS_MET=$1
 | |
|                 ;;
 | |
|                 --progress)
 | |
|                     Progress "."
 | |
|                 ;;
 | |
|                 --root-only)
 | |
|                     shift
 | |
|                     if [ "$1" = "YES" -o "$1" = "yes" ]; then
 | |
|                           ROOT_ONLY=1
 | |
|                       elif [ "$1" = "NO" -o "$1" = "no" ]; then
 | |
|                           ROOT_ONLY=0
 | |
|                       else
 | |
|                         Debug "Invalid option for --root-only parameter of Register function"
 | |
|                     fi
 | |
|                 ;;
 | |
|                 --test-no)
 | |
|                     shift
 | |
|                     TEST_NO=$1
 | |
|                 ;;
 | |
|                 --weight)
 | |
|                     shift
 | |
|                     TEST_WEIGHT=$1
 | |
|                 ;;
 | |
| 
 | |
|                 *)
 | |
|                     echo "INVALID OPTION (Register): $1"
 | |
|                     exit 1
 | |
|                 ;;
 | |
|             esac
 | |
|             # Go to next parameter
 | |
|             shift
 | |
|         done
 | |
| 
 | |
|         # Skip if a test is root only and we are running a non-privileged test
 | |
|         if [ ${ROOT_ONLY} -eq 1 -a ! ${MYID} = "0" ]; then
 | |
|             SKIPTEST=1; SKIPREASON="This test needs root permissions"
 | |
|             SKIPPED_TESTS_ROOTONLY="${SKIPPED_TESTS_ROOTONLY}====${TEST_NO}:space:-:space:${TEST_DESCRIPTION}"
 | |
|             #SkipTest "${TEST_NO}:Test:space:requires:space:root:space:permissions:-:-:"
 | |
|         fi
 | |
| 
 | |
|         # Skip test if it's configured in profile (old style)
 | |
|         if [ ${SKIPTEST} -eq 0 ]; then
 | |
|             FIND=`echo "${TEST_SKIP_ALWAYS}" | grep "${TEST_NO}"`
 | |
|             if [ ! "${FIND}" = "" ]; then SKIPTEST=1; SKIPREASON="Skipped by configuration"; fi
 | |
|         fi
 | |
| 
 | |
|         # Check if this test is on the list to skip
 | |
|         if [ ${SKIPTEST} -eq 0 ]; then
 | |
|             for I in ${SKIP_TESTS}; do
 | |
|                 if [ "${I}" = "${TEST_NO}" ]; then SKIPTEST=1; SKIPREASON="Skipped by configuration (skip-test)"; fi
 | |
|             done
 | |
|         fi
 | |
| 
 | |
|         # Skip if test is not in the list
 | |
|         if [ ${SKIPTEST} -eq 0 -a ! "${TESTS_TO_PERFORM}" = "" ]; then
 | |
|           FIND=`echo "${TESTS_TO_PERFORM}" | grep "${TEST_NO}"`
 | |
|           if [ "${FIND}" = "" ]; then SKIPTEST=1; SKIPREASON="Test not in list of tests to perform"; fi
 | |
|         fi
 | |
| 
 | |
|         # Do not run scans which have a higher intensity than what we prefer
 | |
|         if [ ${SKIPTEST} -eq 0 -a "${TEST_WEIGHT}" = "H" -a "${SCAN_TEST_HEAVY}" = "NO" ]; then SKIPTEST=1; SKIPREASON="Test to system intensive for scan mode (H)"; fi
 | |
|         if [ ${SKIPTEST} -eq 0 -a "${TEST_WEIGHT}" = "M" -a "${SCAN_TEST_MEDIUM}" = "NO" ]; then SKIPTEST=1; SKIPREASON="Test to system intensive for scan mode (M)"; fi
 | |
| 
 | |
|         # Skip test if OS is different than requested
 | |
|         if [ ${SKIPTEST} -eq 0 -a ! -z "${TEST_NEED_OS}" -a ! "${OS}" = "${TEST_NEED_OS}" ]; then
 | |
|             SKIPTEST=1; SKIPREASON="Incorrect guest OS (${TEST_NEED_OS} only)"
 | |
|             if [ ${LOG_INCORRECT_OS} -eq 0 ]; then
 | |
|               SKIPLOGTEST=1
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         # Check for correct hardware platform
 | |
|         if [ ${SKIPTEST} -eq 0 -a ! -z "${TEST_NEED_PLATFORM}" -a ! "${HARDWARE}" = "${TEST_NEED_PLATFORM}" ]; then SKIPTEST=1; SKIPREASON="Incorrect hardware platform"; fi
 | |
| 
 | |
|         # Not all prerequisites met, like missing tool
 | |
|         if [ ${SKIPTEST} -eq 0 -a "${PREQS_MET}" = "NO" ]; then SKIPTEST=1; SKIPREASON="Prerequisities not met (ie missing tool, other type of Linux distribution)"; fi
 | |
| 
 | |
|         # Skip test?
 | |
|         if [ ${SKIPTEST} -eq 0 ]; then
 | |
|             # First wait X seconds (depending pause_between_tests)
 | |
|             if [ ${TEST_PAUSE_TIME} -gt 0 ]; then sleep ${TEST_PAUSE_TIME}; fi
 | |
| 
 | |
|             # Increase counter for every registered test which is performed
 | |
|             counttests
 | |
|             if [ ${SKIPLOGTEST} -eq 0 ]; then LogText "Performing test ID ${TEST_NO} ($TEST_DESCRIPTION)"; fi
 | |
|             TESTS_EXECUTED="${TEST_NO}|${TESTS_EXECUTED}"
 | |
|           else
 | |
|             if [ ${SKIPLOGTEST} -eq 0 ]; then LogText "Skipped test ${TEST_NO} ($TEST_DESCRIPTION)"; fi
 | |
|             if [ ${SKIPLOGTEST} -eq 0 ]; then LogText "Reason to skip: ${SKIPREASON}"; fi
 | |
|             TESTS_SKIPPED="${TEST_NO}|${TESTS_SKIPPED}"
 | |
|         fi
 | |
| 
 | |
|       }
 | |
| 
 | |
|     # Remove PID file
 | |
|     RemovePIDFile()
 | |
|       {
 | |
|         # Test if PIDFILE is defined, before checking file presence
 | |
|         if [ ! "${PIDFILE}" = "" ]; then
 | |
|           if [ -f ${PIDFILE} ]; then
 | |
|               rm -f $PIDFILE;
 | |
|               LogText "PID file removed (${PIDFILE})"
 | |
|             else
 | |
|               LogText "PID file not found (${PIDFILE})"
 | |
|           fi
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Remove any temporary files
 | |
|     RemoveTempFiles()
 | |
|       {
 | |
|         if [ ! "${TEMP_FILES}" = "" ]; then
 | |
|             LogText "Temporary files: ${TEMP_FILES}"
 | |
|             # Clean up temp files
 | |
|             for FILE in ${TEMP_FILES}; do
 | |
|                 # Temporary files should be in /tmp
 | |
|                 TMPFILE=`echo ${FILE} | egrep "^/tmp/lynis" | grep -v "\.\."`
 | |
|                 if [ ! "${TMPFILE}" = "" ]; then
 | |
|                     if [ -f ${TMPFILE} ]; then
 | |
|                         LogText "Action: removing temporary file ${TMPFILE}"
 | |
|                         rm -f ${TMPFILE}
 | |
|                       else
 | |
|                         LogText "Info: temporary file ${TMPFILE} was already removed"
 | |
|                     fi
 | |
|                   else
 | |
|                     LogText "Found invalid temporary file (${FILE}), not removed. Check your /tmp directory."
 | |
|                 fi
 | |
|             done
 | |
|           else
 | |
|             LogText "No temporary files to be deleted"
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Dump to report file
 | |
|     Report()
 | |
|       {
 | |
|         echo "$1" >> ${REPORTFILE}
 | |
|       }
 | |
| 
 | |
|     # Old alias for Report function (will be deprecated)
 | |
|     report()
 | |
|       {
 | |
|         Report "$1"
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : ReportDetails
 | |
|     # Description : Adds specific details to the report, in particular when many
 | |
|     #               smaller atomic tests are performed. For example sysctl keys,
 | |
|     #               and SSH settings.
 | |
|     # Returns     : nothing
 | |
|     ################################################################################
 | |
| 
 | |
|     ReportDetails() {
 | |
|         while [ $# -ge 1 ]; do
 | |
|             case $1 in
 | |
|                 --description)
 | |
|                     shift
 | |
|                     TEST_DESCRIPTION=$1
 | |
|                 ;;
 | |
|                 --field)
 | |
|                     shift
 | |
|                     TEST_FIELD=$1
 | |
|                 ;;
 | |
|                 --preferredvalue|--preferred-value)
 | |
|                     shift
 | |
|                     TEST_PREFERRED_VALUE=$1
 | |
|                 ;;
 | |
|                 # Other details
 | |
|                 --other)
 | |
|                     shift
 | |
|                     TEST_OTHER=$1
 | |
|                 ;;
 | |
|                 --service)
 | |
|                     shift
 | |
|                     TEST_SERVICE=$1
 | |
|                 ;;
 | |
|                 --test)
 | |
|                     shift
 | |
|                     TEST_ID=$1
 | |
|                 ;;
 | |
|                 --value)
 | |
|                     shift
 | |
|                     TEST_VALUE=$1
 | |
|                 ;;
 | |
| 
 | |
|                 *)
 | |
|                     echo "INVALID OPTION (ReportDetails): $1"
 | |
|                     ExitFatal
 | |
|                 ;;
 | |
|             esac
 | |
|             shift # Go to next parameter
 | |
|         done
 | |
|         Report "details[]=${TEST_ID}|service:${TEST_SERVICE}|desc:${TEST_DESCRIPTION};field:${TEST_FIELD};prefval:${TEST_PREFERRED_VALUE};value:${TEST_VALUE};other:${TEST_OTHER}|"
 | |
|     }
 | |
| 
 | |
|     # Log exceptions
 | |
|     ReportException()
 | |
|       {
 | |
|         # 1 parameters
 | |
|         # <ID>:<2 char numeric>|text|
 | |
|         Report "exception_event[]=$1|$2|"
 | |
|         LogText "Exception: test has an exceptional event ($1) with text $2"
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Log manual actions to report file
 | |
|     ReportManual()
 | |
|       {
 | |
|         # 1 parameters
 | |
|         # <ID>:<2 char numeric>
 | |
|         Report "manual_event[]=$1"
 | |
|         LogText "Manual: one or more manual actions are required for further testing of this control/plugin"
 | |
|       }
 | |
| 
 | |
|     # Report data (TESTID STATUS IMPACT MESSAGE)
 | |
|     ReportResult()
 | |
|       {
 | |
|         if [ $1 = "" ]; then TESTID="UNKNOWN"; fi
 | |
|         # Status: OK, WARNING, NEUTRAL, SUGGESTION
 | |
|         # Impact: HIGH, SEVERE, LOW,
 | |
|         #Report "result[]=TESTID-${TESTID},STATUS-$2,IMPACT-$3,MESSAGE-$4-"
 | |
|         # Reset ID before next test
 | |
|         TESTID=""
 | |
|       }
 | |
| 
 | |
|     # Log suggestions to report file
 | |
|     ReportSuggestion()
 | |
|       {
 | |
|         TOTAL_SUGGESTIONS=`expr ${TOTAL_SUGGESTIONS} + 1`
 | |
|         # 4 parameters
 | |
|         # <ID> <Suggestion> <Details> <Solution>
 | |
|         # <ID>         Lynis ID (use CUST-.... for your own tests)
 | |
|         # <Suggestion> Suggestion text to be displayed
 | |
|         # <Details>    Specific item or details
 | |
|         # <Solution>   Optional link for additional information:
 | |
|         #              * url:http://site/link
 | |
|         #              * text:Additional explanation
 | |
|         #              * - for none
 | |
|         if [ "$1" = "" ]; then TEST="UNKNOWN"; else TEST="$1"; fi
 | |
|         if [ "$2" = "" ]; then MESSAGE="UNKNOWN"; else MESSAGE="$2"; fi
 | |
|         if [ "$3" = "" ]; then DETAILS="-"; else DETAILS="$3"; fi
 | |
|         if [ "$4" = "" ]; then SOLUTION="-"; else SOLUTION="$4"; fi
 | |
|         Report "suggestion[]=${TEST}|${MESSAGE}|${DETAILS}|${SOLUTION}|"
 | |
|         LogText "Suggestion: ${MESSAGE} [test:$1] [details:${DETAILS}] [solution:${SOLUTION}]"
 | |
|       }
 | |
| 
 | |
|     # Log warning to report file
 | |
|     ReportWarning()
 | |
|       {
 | |
|         TOTAL_WARNINGS=`expr ${TOTAL_WARNINGS} + 1`
 | |
|         # Old style
 | |
|         # <ID> <priority/impact> <warning text>
 | |
|         if [ "$2" = "L" -o "$2" = "M" -o "$2" = "H" ]; then
 | |
|             DETAILS="$2"
 | |
|             MESSAGE="$3"
 | |
|             TEST="$1"
 | |
|             SOLUTION="-"
 | |
|           else
 | |
|             # New style warning format:
 | |
|             # <ID> <Warning> <Details> <Solution>
 | |
|             #
 | |
|             # <ID>         Lynis ID (use CUST-.... for your own tests)
 | |
|             # <Warning>    Warning text to be displayed
 | |
|             # <Details>    Specific item or details
 | |
|             # <Solution>   Optional link for additional information:
 | |
|             #              * url:http://site/link
 | |
|             #              * text:Additional explanation
 | |
|             #              * - for none
 | |
|             if [ "$1" = "" ]; then TEST="UNKNOWN"; else TEST="$1"; fi
 | |
|             if [ "$2" = "" ]; then MESSAGE="UNKNOWN"; else MESSAGE="$2"; fi
 | |
|             if [ "$3" = "" ]; then DETAILS="-"; else DETAILS="$3"; fi
 | |
|             if [ "$4" = "" ]; then SOLUTION="-"; else SOLUTION="$4"; fi
 | |
|         fi
 | |
|         Report "warning[]=${TEST}|${MESSAGE}|${DETAILS}|${SOLUTION}|"
 | |
|         LogText "Warning: ${MESSAGE} [test:${TEST}] [details:${DETAILS}] [solution:${SOLUTION}]"
 | |
| 
 | |
|       }
 | |
| 
 | |
|     SafePerms()
 | |
|       {
 | |
|         PERMS_OK=0
 | |
|         LogText "Checking permissions of $1"
 | |
|         if [ $# -eq 1 ]; then
 | |
|             IS_PARAMETERS_FILE=`echo $1 | grep "/parameters"`
 | |
|             # Check file permissions
 | |
|               if [ ! -f "$1" ]; then
 | |
|                   LogText "Fatal error: file $1 does not exist. Quitting."
 | |
|                   echo "Fatal error: file $1 does not exist"
 | |
|                   ExitFatal
 | |
|                 else
 | |
|                   PERMS=`ls -l $1`
 | |
|                   # Owner permissions
 | |
|                   OWNER=`echo ${PERMS} | awk -F" " '{ print $3 }'`
 | |
|                   OWNERID=`ls -n $1 | awk -F" " '{ print $3 }'`
 | |
|                   if [ ${PENTESTINGMODE} -eq 0 -a "${IS_PARAMETERS_FILE}" = "" ]; then
 | |
|                       if [ ! "${OWNER}" = "root" -a ! "${OWNERID}" = "0" ]; then
 | |
|                           echo "Fatal error: file $1 should be owned by user 'root' when running it as root (found: ${OWNER})."
 | |
|                           ExitFatal
 | |
|                       fi
 | |
|                     else
 | |
|                       LogText "Note: Owner permissions of file $1 to be expected similar as the UID executing the process"
 | |
|                   fi
 | |
|                   # Group permissions
 | |
|                   GROUP=`echo ${PERMS} | awk -F" " '{ print $4 }'`
 | |
|                   GROUPID=`ls -n $1 | awk -F" " '{ print $4 }'`
 | |
| 
 | |
|                   if [ ${PENTESTINGMODE} -eq 0 -a "${IS_PARAMETERS_FILE}" = "" ]; then
 | |
|                       if [ ! "${GROUP}" = "root" -a ! "${GROUP}" = "wheel" -a ! "${GROUPID}" = "0" ]; then
 | |
|                           echo "Fatal error: group owner of directory $1 should be owned by root user, wheel or similar (found: ${GROUP})."
 | |
|                           ExitFatal
 | |
|                       fi
 | |
|                     else
 | |
|                       LogText "Note: Group permissions of file $1 to be expected similar as the UID executing the process"
 | |
|                   fi
 | |
|                   # Other permissions
 | |
|                   OTHER_PERMS=`echo ${PERMS} | cut -c8-10`
 | |
|                   if [ ! "${OTHER_PERMS}" = "---" -a ! "${OTHER_PERMS}" = "r--" ]; then
 | |
|                       echo "Fatal error: permissions of file $1 are not strict enough. Access to 'other' should be denied or read-only."
 | |
|                       ExitFatal
 | |
|                   fi
 | |
|                   # Set PERMS_OK to 1 if no fatal errors occurred
 | |
|                   PERMS_OK=1
 | |
|                   LogText "File permissions are OK"
 | |
|               fi
 | |
|           else
 | |
|             LogText "Fatal error: invalid amount of parameters when calling function SafePerms()"
 | |
|             echo "Invalid amount of parameters for function SafePerms()"
 | |
|             ExitFatal
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : SearchItem()
 | |
|     # Description : Search if a specific string exists in in a file
 | |
|     # Parameters  : $1 = search string
 | |
|     #             : $2 = file
 | |
|     # Returns     : <nothing>
 | |
|     ################################################################################
 | |
| 
 | |
|     SearchItem()
 | |
|       {
 | |
|          ITEM_FOUND=0
 | |
|          if [ $# -eq 2 ]; then
 | |
|             # Don't search in /dev/null, it's too empty there
 | |
|             if [ -f $2 ]; then
 | |
|                 # Check if we can find the main type (with or without brackets)
 | |
|                 LogText "Test: search string $1 in file $2"
 | |
|                 FIND=`egrep "$1" $2`
 | |
|                 if [ ! "${FIND}" = "" ]; then
 | |
|                     ITEM_FOUND=1
 | |
|                     LogText "Result: found string"
 | |
|                     LogText "Full string: ${FIND}"
 | |
|                  else
 | |
|                     LogText "Result: search string NOT found"
 | |
|                 fi
 | |
|               else
 | |
|                 LogText "Skipping search, file does not exist"
 | |
|                 ReportException ${TEST_NO} "Test is trying to search for a string in nonexistent file"
 | |
|             fi
 | |
|           else
 | |
|             ReportException ${TEST_NO} "Error in function call to CheckItem"
 | |
|          fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     # Show result code
 | |
|     ShowResult()
 | |
|       {
 | |
|         case $1 in
 | |
|         OK)
 | |
|             echo "[ ${OK}OK${NORMAL} ]"
 | |
|         ;;
 | |
|         WARNING)
 | |
|             echo "[ ${WARNING}WARNING${NORMAL} ]"
 | |
|             # log the warning to our log file
 | |
|             #LogText "Warning: $2"
 | |
|             # add the warning to our report file
 | |
|             #Report "warning=$2"
 | |
|         ;;
 | |
|         esac
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : ShowComplianceFinding()
 | |
|     # Description : Display a section of a compliance standard which is not fulfilled
 | |
|     # Parameters  : <misc>
 | |
|     # Returns     : Nothing
 | |
|     ################################################################################
 | |
| 
 | |
|     ShowComplianceFinding()
 | |
|       {
 | |
|         REASON=""
 | |
|         STANDARD_NAME=""
 | |
|         STANDARD_VERSION=""
 | |
|         STANDARD_SECTION=""
 | |
|         STANDARD_SECTION_TITLE=""
 | |
|         ACTUAL_VALUE=""
 | |
|         EXPECTED_VALUE=""
 | |
|         while [ $# -ge 1 ]; do
 | |
|             case $1 in
 | |
|                 --standard)
 | |
|                     shift
 | |
|                     STANDARD_NAME=$1
 | |
|                 ;;
 | |
|                 --version)
 | |
|                     shift
 | |
|                     STANDARD_VERSION=$1
 | |
|                 ;;
 | |
|                 --section)
 | |
|                     shift
 | |
|                     STANDARD_SECTION=$1
 | |
|                 ;;
 | |
|                 --section-title)
 | |
|                     shift
 | |
|                     STANDARD_SECTION_TITLE=$1
 | |
|                 ;;
 | |
|                 --reason)
 | |
|                     shift
 | |
|                     REASON=$1
 | |
|                 ;;
 | |
|                 --actual)
 | |
|                     shift
 | |
|                     ACTUAL_VALUE=$1
 | |
|                 ;;
 | |
|                 --expected)
 | |
|                     shift
 | |
|                     EXPECTED_VALUE=$1
 | |
|                 ;;
 | |
| 
 | |
|                 *)
 | |
|                     echo "INVALID OPTION (ShowComplianceFinding): $1"
 | |
|                     exit 1
 | |
|                 ;;
 | |
|             esac
 | |
|             # Go to next parameter
 | |
|             shift
 | |
|         done
 | |
|         # Should we show this non-compliance on screen?
 | |
|         SHOW=0
 | |
|         case ${STANDARD_NAME} in
 | |
|             cis)
 | |
|                 if [ ${COMPLIANCE_ENABLE_CIS} -eq 1 ]; then SHOW=1; fi
 | |
|                 STANDARD_FRIENDLY_NAME="CIS"
 | |
|             ;;
 | |
|             hipaa)
 | |
|                 if [ ${COMPLIANCE_ENABLE_HIPAA} -eq 1 ]; then SHOW=1; fi
 | |
|                 STANDARD_FRIENDLY_NAME="HIPAA"
 | |
|             ;;
 | |
|             iso27001)
 | |
|                 if [ ${COMPLIANCE_ENABLE_ISO27001} -eq 1 ]; then SHOW=1; fi
 | |
|                 STANDARD_FRIENDLY_NAME="ISO27001"
 | |
|             ;;
 | |
|             pci-dss)
 | |
|                 if [ ${COMPLIANCE_ENABLE_PCI_DSS} -eq 1 ]; then SHOW=1; fi
 | |
|                 STANDARD_FRIENDLY_NAME="PCI DSS"
 | |
|             ;;
 | |
|         esac
 | |
|         # Only display if standard is enabled in the profile and mark system as non-compliant
 | |
|         if [ ${SHOW} -eq 1 ]; then
 | |
|             COMPLIANCE_FINDINGS_FOUND=1
 | |
|             DisplayManual "  [${WHITE}${STANDARD_FRIENDLY_NAME} ${STANDARD_VERSION}${NORMAL}] - ${CYAN}Section ${STANDARD_SECTION}${NORMAL} - ${WHITE}${STANDARD_SECTION_TITLE}${NORMAL}"
 | |
|             DisplayManual "  - Details:        ${REASON}"
 | |
|             DisplayManual "  - Configuration:  ${RED}${ACTUAL_VALUE}${NORMAL} / ${EXPECTED_VALUE}"
 | |
|             DisplayManual ""
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : ShowSymlinkPath()
 | |
|     # Description : Check if we can find the path behind a symlink
 | |
|     # Parameters  : $1 = file
 | |
|     # Returns     : FOUNDPATH (0 not found, 1 found path)
 | |
|     ################################################################################
 | |
| 
 | |
|     ShowSymlinkPath()
 | |
|       {
 | |
|         sFILE=$1
 | |
|         FOUNDPATH=0
 | |
|         SYMLINK_USE_PYTHON=0
 | |
|         SYMLINK_USE_READLINK=0
 | |
|         # Check for symlink
 | |
|         if [ -L ${sFILE} ]; then
 | |
| 
 | |
|                 # Mac OS does not know -f option, nor do some others
 | |
|                 if [ "${OS}" = "MacOS" ]; then
 | |
|                     # If a Python binary is found, use the one in path
 | |
|                     if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${PYTHONBINARY}" = "" ]; then
 | |
|                         FIND=`which python 2> /dev/null`
 | |
|                         if [ ! "${FIND}" = "" ]; then LogText "Setting temporary pythonbinary variable"; PYTHONBINARY="${FIND}"; fi
 | |
|                     fi
 | |
|                     if [ ! "${PYTHONBINARY}" = "" ]; then
 | |
|                         SYMLINK_USE_PYTHON=1
 | |
|                         LogText "Note: using Python to determine symlinks"
 | |
|                         tFILE=`python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" $1`
 | |
|                     fi
 | |
|                   else
 | |
|                     if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${READLINKBINARY}" = "" ]; then
 | |
|                         FIND=`which readlink 2> /dev/null`
 | |
|                         if [ ! "${FIND}" = "" ]; then LogText "Setting temporary readlinkbinary variable"; READLINKBINARY="${FIND}"; fi
 | |
|                     fi
 | |
| 
 | |
|                     if [ ! "${READLINKBINARY}" = "" ]; then
 | |
|                         SYMLINK_USE_READLINK=1
 | |
|                         LogText "Note: Using real readlink binary to determine symlinks"
 | |
|                         tFILE=`${READLINKBINARY} -f ${sFILE}`
 | |
|                         LogText "Result: readlink shows ${tFILE} as output"
 | |
|                     fi
 | |
|                 fi
 | |
|                 # Check if we can find the file now
 | |
|                 if [ "${tFILE}" = "" ]; then
 | |
|                     LogText "Result: command did not return any value"
 | |
|                 elif [ -f ${tFILE} ]; then
 | |
|                     sFILE="${tFILE}"
 | |
|                     LogText "Result: symlink found, pointing to file ${sFILE}"
 | |
|                     FOUNDPATH=1
 | |
|                 elif [ -b ${tFILE} ]; then
 | |
|                     sFILE="${tFILE}"
 | |
|                     LogText "Result: symlink found, pointing to block device ${sFILE}"
 | |
|                     FOUNDPATH=1
 | |
|                 elif [ -c ${tFILE} ]; then
 | |
|                     sFILE="${tFILE}"
 | |
|                     LogText "Result: symlink found, pointing to character device ${sFILE}"
 | |
|                     FOUNDPATH=1
 | |
|                 elif [ -d ${tFILE} ]; then
 | |
|                     sFILE="${tFILE}"
 | |
|                     LogText "Result: symlink found, pointing to directory ${sFILE}"
 | |
|                     FOUNDPATH=1
 | |
|                   else
 | |
|                     # Check the full path of the symlink, strip the filename, copy the path and linked filename together
 | |
|                     tDIR=`echo ${sFILE} | awk '{match($1, "^.*/"); print substr($1, 1, RLENGTH-1)}'`
 | |
|                     tFILE="${tDIR}/${tFILE}"
 | |
|                     if [ -L ${tFILE} ]; then
 | |
|                         LogText "Result: this symlink links to another symlink"
 | |
|                         # Ensure that we use a second try with the right tool as well
 | |
|                         if [ ${SYMLINK_USE_PYTHON} -eq 1 ]; then
 | |
|                             tFILE=`python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" ${tFILE}`
 | |
|                         elif [ ${SYMLINK_USE_READLINK} -eq 1 ]; then
 | |
|                             tFILE=`${READLINKBINARY} -f ${tFILE}`
 | |
|                         fi
 | |
|                         # Check if we now have a normal file
 | |
|                         if [ -f ${tFILE} ]; then
 | |
|                             sFILE="${tFILE}"
 | |
|                             LogText "Result: symlink finally found, seems to be file ${sFILE}"
 | |
|                             FOUNDPATH=1
 | |
|                         elif [ -d ${tFILE} ]; then
 | |
|                             sFILE="${tFILE}"
 | |
|                             LogText "Result: symlink finally found, seems to be directory ${sFILE}"
 | |
|                             FOUNDPATH=1
 | |
|                         else
 | |
|                            LogText "Result: could not find file ${tFILE}, most likely too complicated symlink or too often linked"
 | |
|                         fi
 | |
|                     elif [ -f ${tFILE} ]; then
 | |
|                         sFILE="${tFILE}"
 | |
|                         LogText "Result: symlink found, seems to be file ${sFILE}"
 | |
|                         FOUNDPATH=1
 | |
|                     elif [ -d ${tFILE} ]; then
 | |
|                         sFILE="${tFILE}"
 | |
|                         LogText "Result: symlink found, seems to be directory ${sFILE}"
 | |
|                         FOUNDPATH=1
 | |
|                     else
 | |
|                         LogText "Result: file ${tFILE} in ${tDIR} not found"
 | |
|                     fi
 | |
|                 fi
 | |
|           else
 | |
|             LogText "Result: file not a symlink"
 | |
|         fi
 | |
|         # Now check if our new location is actually a file or directory destination
 | |
|         if [ -L ${sFILE} ]; then
 | |
|             LogText "Result: unable to determine symlink, or location ${sFILE} is just another symlink"
 | |
|             FOUNDPATH=0
 | |
|         fi
 | |
|         if [ ${FOUNDPATH} -eq 1 ]; then
 | |
|             SYMLINK="${sFILE}"
 | |
|           else
 | |
|             SYMLINK=""
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : SkipAtomicTest
 | |
|     # Description : Test if an atomic test should be skipped
 | |
|     # Returns     : 0 (True) or 1 (False)
 | |
|     # Usage       : if SkipAtomicTest "SSH-7408:permitrootlogin"; then echo "Skip this atomic test"; fi
 | |
|     ################################################################################
 | |
| 
 | |
|     SkipAtomicTest() {
 | |
|         RETVAL=255
 | |
|         if [ $# -eq 1 ]; then
 | |
|             RETVAL=1
 | |
|             # Check if this test is on the list to skip
 | |
|             for I in ${SKIP_TESTS}; do
 | |
|                 if [ "${I}" = "$1" ]; then RETVAL=0; LogText "Atomic test skipped by configuration (skip-test)"; fi
 | |
|             done
 | |
|         fi
 | |
|         return $RETVAL
 | |
|     }
 | |
| 
 | |
| 
 | |
|     ################################################################################
 | |
|     # Name        : TestValue
 | |
|     # Description : Test if a value is good/bad (e.g. according to best practices)
 | |
|     # Returns     : 0 (True) or 1 (False)
 | |
|     # Usage       : if TestValue --function contains --value "Full Text" --search "Text"; then echo "Found!"; fi
 | |
|     ################################################################################
 | |
| 
 | |
|     TestValue()
 | |
|       {
 | |
|         local FIND=""
 | |
|         local VALUE=""
 | |
|         local RESULT=""
 | |
|         local SEARCH=""
 | |
|         while [ $# -ge 1 ]; do
 | |
|             case $1 in
 | |
|                 --function)
 | |
|                     shift
 | |
|                     local FUNCTION=$1
 | |
|                 ;;
 | |
|                 --value)
 | |
|                     shift
 | |
|                     VALUE=$1
 | |
|                 ;;
 | |
|                 --search)
 | |
|                     shift
 | |
|                     SEARCH=$1
 | |
|                 ;;
 | |
|                 *)
 | |
|                     echo "INVALID OPTION USED (TestValue): $1"
 | |
|                     exit 1
 | |
|                 ;;
 | |
|             esac
 | |
|             # Go to next parameter
 | |
|             shift
 | |
|         done
 | |
| 
 | |
|         if [ "${VALUE}" = "" ]; then echo "No value provided to function (TestValue)"; fi
 | |
| 
 | |
|         # Apply the related function
 | |
|         case ${FUNCTION} in
 | |
|               "contains")
 | |
|                   FIND=`echo ${VALUE} | egrep "${SEARCH}"`
 | |
|                   if [ "${FIND}" = "" ]; then RESULT=1; else RESULT=0; fi
 | |
|               ;;
 | |
|               #"gt" | "greater-than")   COLOR=$GREEN   ;;
 | |
|               #"equals")     COLOR=$RED     ;;
 | |
|               #"not-equal")   COLOR=$WHITE   ;;
 | |
|               #"lt" | "less-than")  COLOR=$YELLOW  ;;
 | |
|               *) echo "INVALID OPTION USED (TestValue, parameter of function: $1)"; exit 1 ;;
 | |
|         esac
 | |
| 
 | |
|         if [ ! "${RESULT}" = "" ]; then
 | |
|             return ${RESULT}
 | |
|           else
 | |
|             echo "ERROR: No result returned from function (TestValue). Incorrect usage?"; exit 1
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     ViewCategories()
 | |
|       {
 | |
|         if [ ! "${INCLUDEDIR}" = "" ]; then
 | |
|             InsertSection "Available test categories"
 | |
|             for I in `ls ${INCLUDEDIR}/tests_* | xargs -n 1 basename | sed 's/tests_//' | grep -v "custom.template"`; do
 | |
|               echo "  - ${I}"
 | |
|             done
 | |
|         fi
 | |
|         echo ""
 | |
|         exit 0
 | |
|       }
 | |
| 
 | |
|     # Wait for [ENTER] or manually break
 | |
|     wait_for_keypress()
 | |
|       {
 | |
|         if [ ! ${QUICKMODE} -eq 1 ]; then
 | |
|           echo ""; echo "[ ${WHITE}Press [ENTER] to continue, or [CTRL]+C to stop${NORMAL} ]"
 | |
|           read void
 | |
|         fi
 | |
|       }
 | |
| 
 | |
|     # Wait for [ENTER] or manually break
 | |
|     WaitForKeypress()
 | |
|       {
 | |
|         if [ ! ${QUICKMODE} -eq 1 ]; then
 | |
|           echo ""; echo "[ ${WHITE}Press [ENTER] to continue, or [CTRL]+C to stop${NORMAL} ]"
 | |
|           read void
 | |
|         fi
 | |
|       }
 | |
| 
 | |
| 
 | |
| #================================================================================
 | |
| # Lynis is part of Lynis Enterprise and released under GPLv3 license
 | |
| # Copyright 2007-2016 - Michael Boelen, CISOfy - https://cisofy.com
 |