"Fossies" - the Fresh Open Source Software Archive

Member "lynis/include/functions" (22 Jul 2021, 160606 Bytes) of package /linux/misc/lynis-3.0.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bash source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "functions": 3.0.5_vs_3.0.6.

    1 #!/bin/sh
    2 
    3 #################################################################################
    4 #
    5 #   Lynis
    6 # ------------------
    7 #
    8 # Copyright 2007-2013, Michael Boelen
    9 # Copyright 2007-2021, CISOfy
   10 #
   11 # Website  : https://cisofy.com
   12 # Blog     : http://linux-audit.com
   13 # GitHub   : https://github.com/CISOfy/lynis
   14 #
   15 # Lynis comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
   16 # welcome to redistribute it under the terms of the GNU General Public License.
   17 # See LICENSE file for usage of this software.
   18 #
   19 #################################################################################
   20 #
   21 # Functions
   22 #
   23 #################################################################################
   24 #
   25 #    Function                   Description
   26 #    -----------------------    -------------------------------------------------
   27 #    AddHP                      Add Hardening points to plot a graph later
   28 #    AddSetting                 Addition of setting
   29 #    AddSystemGroup             Adds a system to a group
   30 #    CheckFilePermissions       Check file permissions
   31 #    CheckItem                  Test for presence of a string in report file
   32 #    CheckUpdates               Determine if a new version of Lynis is available
   33 #    CleanUp                    Clean up files before closing program
   34 #    CountTests                 Count number of performed tests
   35 #    ContainsString             Find the needle (string) in the haystack (another string)
   36 #    CreateTempFile             Create a temporary file
   37 #    Debug                      Display additional information on the screen (not suited for cronjob)
   38 #    DigitsOnly                 Return only the digits from a string
   39 #    DirectoryExists            Check if a directory exists on the disk
   40 #    DiscoverProfiles           Determine available profiles on system
   41 #    Display                    Output text to screen with colors and indentation
   42 #    DisplayError               Show an error on screen
   43 #    DisplayException           Show an exception on screen
   44 #    DisplayManual              Output text to screen without any layout
   45 #    DisplayToolTip             Show a tip for improving usage of the tool
   46 #    DisplayWarning             Show a clear warning on screen
   47 #    Equals                     Compares two strings
   48 #    ExitClean                  Stop the program (cleanly), with exit code 0
   49 #    ExitCustom                 Stop the program (cleanly), with custom exit code
   50 #    ExitFatal                  Stop the program (cleanly), with exit code 1
   51 #    FileExists                 Check if a file exists on the disk
   52 #    FileInstalledByPackage     Check if a file is linked to a package
   53 #    FileIsEmpty                Check if a file is empty
   54 #    FileIsReadable             Check if a file is readable or directory accessible
   55 #    GetHostID                  Retrieve an unique ID for this host
   56 #    GetReportData              Request data from report
   57 #    HasCorrectFilePermissions  Check file permissions and see if they match expected values
   58 #    HasData                    Checks for data in variable
   59 #    InsertSection              Insert a section block
   60 #    InsertPluginSection        Insert a section block for plugins
   61 #    IsContainer                Determine if program runs in a container
   62 #    IsDebug                    Check if --debug is used
   63 #    IsDeveloperMode            Check if --developer is used
   64 #    IsDeveloperVersion         Check if program is a developer release
   65 #    IsEmpty                    Check for empty result or variable
   66 #    IsNotebook                 System detection
   67 #    IsOwnedByRoot              Determine if file or directory is owned by root
   68 #    IsRunning                  Check if a process is running
   69 #    IsVerbose                  Check if --verbose is used
   70 #    IsVirtualMachine           Check if this system is a virtual machine
   71 #    IsWorldExecutable          Check if a file is world executable
   72 #    IsWorldReadable            Check if a file is world readable
   73 #    IsWorldWritable            Check if a file is world writable
   74 #    LogText                    Log text strings to logfile, prefixed with date/time
   75 #    LogTextBreak               Insert a separator in log file
   76 #    PackageIsInstalled         Test for installed package
   77 #    ParseNginx                 Parse nginx configuration lines
   78 #    ParseProfiles              Parse all available profiles
   79 #    ParseTestValues            Parse a set of values
   80 #    PortIsListening            Check if machine is listening on specified protocol and port
   81 #    Progress                   Show progress on screen
   82 #    Readonly                   Mark a variable as read-only data
   83 #    Register                   Register a test (for logging and execution)
   84 #    RandomString               Show a random string
   85 #    RemoveColors               Reset all colors
   86 #    RemovePIDFile              Remove PID file
   87 #    RemoveTempFiles            Remove temporary files
   88 #    Report                     Add string of data to report file
   89 #    ReportDetails              Store details of tests which include smaller atomic tests in report
   90 #    ReportException            Add an exception to the report file (for debugging purposes)
   91 #    ReportManual               Log manual actions to report file
   92 #    ReportSuggestion           Add a suggestion to report file
   93 #    ReportWarning              Add a warning and priority to report file
   94 #    SafeFile                   Security tests to perform on a file before using it
   95 #    SafePerms                  Check if a file has safe permissions
   96 #    SafeInput                  Test provided string to see if it contains unwanted characters
   97 #    SearchItem                 Search a string in a file
   98 #    ShowComplianceFinding      Display a particular finding regarding compliance or a security standard
   99 #    ShowSymlinkPath            Show a path behind a symlink
  100 #    SkipAtomicTest             Test if a subtest needs to be skipped
  101 #    Status                     Show execution status, such as active test being performed
  102 #    StoreNginxSettings         Save parsed nginx settings to file
  103 #    TestValue                  Evaluate a value in a string or key
  104 #    ViewCategories             Show available category of tests
  105 #    ViewGroups                 Display test groups
  106 #    WaitForKeyPress            Wait for user to press a key to continue
  107 #
  108 #################################################################################
  109 
  110 
  111     ################################################################################
  112     # Name        : AddHP()
  113     # Description : Add hardening points and count them
  114     #
  115     # Parameters  : $1 = points to add (0 or higher)
  116     #               $2 = maximum points (at least value of $1 or higher)
  117     # Returns     : <nothing>
  118     # Usage       : AddHP 1 3
  119     ################################################################################
  120 
  121     AddHP() {
  122         HPADD=$1; HPADDMAX=$2
  123         HPPOINTS=$((HPPOINTS + HPADD))
  124         HPTOTAL=$((HPTOTAL + HPADDMAX))
  125         if [ ${HPADD} -eq ${HPADDMAX} ]; then
  126             LogText "Hardening: assigned maximum number of hardening points for this item (${HPADDMAX}). Currently having ${HPPOINTS} points (out of ${HPTOTAL})"
  127         else
  128             LogText "Hardening: assigned partial number of hardening points (${HPADD} of ${HPADDMAX}). Currently having ${HPPOINTS} points (out of ${HPTOTAL})"
  129         fi
  130     }
  131 
  132 
  133     ################################################################################
  134     # Name        : AddSetting()
  135     # Description : Addition of a setting for display with 'lynis show settings'
  136     #
  137     # Parameters  : $1 = setting
  138     #               $2 = value
  139     #               $3 = description
  140     # Returns     : <nothing>
  141     # Usage       : AddSetting debug 1 'Debug mode'
  142     ################################################################################
  143 
  144     AddSetting() {
  145         if [ $# -eq 3 ]; then
  146             SETTING="$1"
  147             VALUE="$2"
  148             DESCRIPTION="$3"
  149             if [ -z "${SETTINGS_FILE}" ]; then
  150                 CreateTempFile
  151                 SETTINGS_FILE="${TEMP_FILE}"
  152             fi
  153             FIND=$(grep -E "^${SETTING};" ${SETTINGS_FILE})
  154             if [ -z "${FIND}" ]; then
  155                 echo "${SETTING};${VALUE};${DESCRIPTION};" >> ${SETTINGS_FILE}
  156             else
  157                 Debug "Setting '${SETTING}' was already configured, overwriting previous line '${FIND}' in ${SETTINGS_FILE} with value '${VALUE}'"
  158                 # Delete line first, then add new value (inline search and replace is messy)
  159                 CreateTempFile
  160                 TEMP_SETTINGS_FILE="${TEMP_FILE}"
  161                 cat ${SETTINGS_FILE} > ${TEMP_SETTINGS_FILE}
  162                 sed -e '/^'"${SETTING}"';/d' ${TEMP_SETTINGS_FILE} > ${SETTINGS_FILE}
  163                 rm "${TEMP_SETTINGS_FILE}"
  164                 echo "${SETTING};${VALUE};${DESCRIPTION};" >> ${SETTINGS_FILE}
  165             fi
  166         else
  167             echo "Error: incorrect call to AddSetting. Needs 3 arguments."
  168         fi
  169     }
  170 
  171 
  172     ################################################################################
  173     # Name        : AddSystemGroup()
  174     # Description : Adds a system to a group, which can be used for categorizing
  175     #
  176     # Parameters  : $1 = group name
  177     # Returns     : <nothing>
  178     # Usage       : AddSystemGroup "test"
  179     ################################################################################
  180 
  181     AddSystemGroup() {
  182         Report "system_group[]=$1"
  183     }
  184 
  185 
  186     ################################################################################
  187     # Name        : CheckFilePermissions()
  188     # Description : Check file permissions
  189     #
  190     # Parameters  : Full path to file or directory
  191     # Returns     : PERMS (FILE_NOT_FOUND | OK | BAD)
  192     # Notes       : This function might be replaced in future
  193     ################################################################################
  194 
  195     CheckFilePermissions() {
  196         CHECKFILE="$1"
  197         if [ ! -d ${CHECKFILE} -a ! -f ${CHECKFILE} ]; then
  198             PERMS="FILE_NOT_FOUND"
  199             FILEVALUE=""
  200         else
  201             # If 'file' is an directory, use -d
  202             if [ -d ${CHECKFILE} ]; then
  203                 FILEVALUE=$(ls -d -l ${CHECKFILE} | cut -c 2-10)
  204                 PROFILEVALUE=$(grep '^permdir' ${PROFILE} | grep "=${CHECKFILE}:" | cut -d: -f2)
  205             else
  206                 FILEVALUE=$(ls -l ${CHECKFILE} | cut -c 2-10)
  207                 PROFILEVALUE=$(grep '^permfile' ${PROFILE} | grep "=${CHECKFILE}:" | cut -d: -f2)
  208             fi
  209             if [ "${FILEVALUE}" = "${PROFILEVALUE}" ]; then PERMS="OK"; else PERMS="BAD"; fi
  210         fi
  211     }
  212 
  213 
  214     ################################################################################
  215     # Name        : CheckItem()
  216     # Description : Check if a specific item exists in the report
  217     #
  218     # Parameters  : $1 = key
  219     #               $2 = value
  220     # Returns     : exit code (0 = True, 1 = False)
  221     # Usage       : if CheckItem "key" "value"; then ....; fi
  222     ################################################################################
  223 
  224     CheckItem() {
  225         RETVAL=255
  226         if [ $# -eq 2 ]; then
  227             # Don't search in /dev/null, it's too empty there
  228             if [ ! "${REPORTFILE}" = "/dev/null" ]; then
  229                 # Check if we can find the main type (with or without brackets)
  230                 LogText "Test: search string $2 in earlier discovered results"
  231                 FIND=$(grep -E "^$1(\[\])?=" ${REPORTFILE} | grep -E "$2")
  232                 if HasData "${FIND}"; then
  233                     RETVAL=0
  234                     LogText "Result: found search string (result: $FIND)"
  235                 else
  236                     LogText "Result: search string NOT found"
  237                     RETVAL=1
  238                 fi
  239             else
  240                 LogText "Skipping search, as /dev/null is being used"
  241             fi
  242             return ${RETVAL}
  243         else
  244             ReportException ${TEST_NO} "Error in function call to CheckItem"
  245         fi
  246     }
  247 
  248 
  249     ################################################################################
  250     # Name        : CheckUpdates()
  251     # Description : Determine if there is an update available
  252     #
  253     # Returns     : <nothing>
  254     # Usage       : CheckUpdates
  255     #               Use PROGRAM_LV (latest version) and compare it with actual version (PROGRAM_AC)
  256     ################################################################################
  257 
  258     CheckUpdates() {
  259         PROGRAM_LV="0000000000"; DB_MALWARE_LV="0000000000"; DB_FILEPERMS_LV="0000000000"
  260         if [ ${RUN_UPDATE_CHECK} -eq 1 ]; then
  261             LYNIS_LV_RECORD="lynis-latest-version.cisofy.com."
  262             FIND=$(which dig 2> /dev/null | grep -v "no [^ ]* in")
  263             if [ -n "${FIND}" ]; then
  264                 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]$")
  265             else
  266                 FIND=$(which host 2> /dev/null | grep -v "no [^ ]* in ")
  267                 if [ -n "${FIND}" ]; then
  268                     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]$")
  269                     if [ "${PROGRAM_LV}" = "" ]; then PROGRAM_LV=0; fi
  270                 else
  271                     FIND=$(which drill 2> /dev/null | grep -v "no [^ ]* in ")
  272                     if [ -n "${FIND}" ]; then
  273                         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]$")
  274                         if [ -z "${PROGRAM_LV}" ]; then PROGRAM_LV=0; fi
  275                     else
  276                         LogText "Result: dig, drill or host not installed, update check skipped"
  277                         UPDATE_CHECK_SKIPPED=1
  278                     fi
  279                 fi
  280             fi
  281         fi
  282     }
  283 
  284 
  285     ################################################################################
  286     # Name        : CleanUp()
  287     # Description : Delete PID and temporary files, stop execution (exit code 1)
  288     #
  289     # Parameters  : <none>
  290     # Returns     : <nothing>
  291     # Usage       : this function is triggered by a manual break by user
  292     ################################################################################
  293 
  294     CleanUp() {
  295         echo ""; echo "Interrupt detected."
  296         RemovePIDFile
  297         RemoveTempFiles
  298         Display --text "Cleaning up..." --result DONE --color GREEN
  299         ExitFatal
  300     }
  301 
  302 
  303     ################################################################################
  304     # Name        : ContainsString()
  305     # Description : Search a specific string (or regular expression) in another
  306     #
  307     # Returns     : exit code (0 = True, 1 = False)
  308     # Usage       : if ContainsString "needle" "there is a needle in the haystack"; echo "Found"; else "Not found"; fi
  309     ################################################################################
  310 
  311     ContainsString() {
  312         RETVAL=1
  313         if [ $# -ne 2 ]; then ReportException "ContainsString" "Incorrect number of arguments for ContainsStrings function"; fi
  314         FIND=$(echo "$2" | grep -E "$1")
  315         if [ ! "${FIND}" = "" ]; then RETVAL=0; fi
  316         return ${RETVAL}
  317     }
  318 
  319 
  320     ################################################################################
  321     # Name        : CountTests()
  322     # Description : Counter for the number of tests performed
  323     #
  324     # Parameters  : <none>
  325     # Returns     : <nothing>
  326     # Usage       : Call CountTests to increase number by 1
  327     ################################################################################
  328 
  329     CountTests() {
  330         CTESTS_PERFORMED=$((CTESTS_PERFORMED + 1))
  331     }
  332 
  333 
  334     ################################################################################
  335     # Name        : CreateTempFile()
  336     # Description : Creates a temporary file
  337     #
  338     # Returns     : TEMP_FILE (variable)
  339     # Usage       : CreateTempFile
  340     #               if [ ! "${TEMP_FILE}" = "" ]; then
  341     #                   MYTMPFILE="${TEMP_FILE}"
  342     #                   echo "My temporary file is ${MYTMPFILE}"
  343     #               fi
  344     ################################################################################
  345 
  346     CreateTempFile() {
  347         TEMP_FILE=""
  348         if [ "${OS}" = "AIX" ]; then
  349             RANDOMSTRING1="lynis-$(od -N4 -tu /dev/random | awk 'NR==1 {print $2} {}')"
  350             TEMP_FILE="/tmp/${RANDOMSTRING1}"
  351             touch ${TEMP_FILE}
  352         else
  353             TEMP_FILE=$(mktemp /tmp/lynis.XXXXXXXXXX) || exit 1
  354         fi
  355         if [ ! "${TEMP_FILE}" = "" ]; then
  356             LogText "Action: created temporary file ${TEMP_FILE}"
  357         else
  358             Fatal "Could not create a temporary file"
  359         fi
  360         # Add temporary file to queue for cleanup later
  361         TEMP_FILES="${TEMP_FILES} ${TEMP_FILE}"
  362     }
  363 
  364 
  365     ################################################################################
  366     # Name        : DirectoryExists()
  367     # Description : Check if a directory exists
  368     #
  369     # Returns     : exit code (0 = True, 1 = False)
  370     # Usage       : if DirectoryExists; then echo "it exists"; else echo "It does not exist"; fi
  371     ################################################################################
  372 
  373     # Determine if a directory exists
  374     DirectoryExists() {
  375         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling DirectoryExists function"; fi
  376         DIRECTORY_FOUND=0
  377         LogText "Test: checking if directory $1 exists"
  378         if [ -d $1 ]; then
  379             LogText "Result: directory $1 exists"
  380             DIRECTORY_FOUND=1
  381             return 0
  382         else
  383             LogText "Result: directory $1 NOT found"
  384             return 1
  385         fi
  386     }
  387 
  388 
  389     ################################################################################
  390     # Name        : Debug()
  391     # Description : Show additional information on screen
  392     #
  393     # Input       : $1 = text
  394     # Returns     : <nothing>
  395     # Usage       : Debug "More details"
  396     ################################################################################
  397 
  398     Debug() {
  399         if [ ${DEBUG} -eq 1 -a $# -gt 0 ]; then echo "${PURPLE}[DEBUG]${NORMAL} $1"; fi
  400     }
  401 
  402 
  403     ################################################################################
  404     # Name        : DigitsOnly()
  405     # Description : Only extract numbers from a string
  406     #
  407     # Returns     : Digits only string (VALUE)
  408     ################################################################################
  409 
  410     DigitsOnly() {
  411         VALUE=$1
  412         LogText "Value is now: ${VALUE}"
  413         if [ ! "${AWKBINARY}" = "" ]; then
  414             VALUE=$(echo ${VALUE} | grep -Eo '[0-9]{1,}')
  415         fi
  416         LogText "Returning value: ${VALUE}"
  417     }
  418 
  419 
  420     ################################################################################
  421     # Name        : DiscoverProfiles()
  422     # Description : Determine which profiles we have available
  423     #
  424     # Returns     : <nothing>
  425     # Usage       : DiscoverProfiles
  426     ################################################################################
  427 
  428     DiscoverProfiles() {
  429         # Try to find a default and custom profile, unless one was specified manually
  430         if [ "${PROFILE}" = "" ]; then
  431             CUSTOM_PROFILE=""
  432             DEFAULT_PROFILE=""
  433             PROFILEDIR=""
  434             tPROFILE_NAMES="default.prf custom.prf"
  435             if [ ${USE_CWD} -eq 1 ]; then
  436                 tPROFILE_TARGETS="."
  437             else
  438                 tPROFILE_TARGETS="/usr/local/etc/lynis /etc/lynis /usr/local/lynis ."
  439             fi
  440             for PNAME in ${tPROFILE_NAMES}; do
  441                 for PLOC in ${tPROFILE_TARGETS}; do
  442                     # Only use one default.prf
  443                     if [ "${PNAME}" = "default.prf" -a ! "${DEFAULT_PROFILE}" = "" ]; then
  444                         Debug "Already discovered default.prf - skipping this file (${PLOC}/${PNAME})"
  445                     elif [ "${PNAME}" = "custom.prf" -a ! "${CUSTOM_PROFILE}" = "" ]; then
  446                         Debug "Already discovered custom.prf - skipping this file (${PLOC}/${PNAME})"
  447                     else
  448                         if [ "${PLOC}" = "." ]; then FILE="${WORKDIR}/${PNAME}"; else FILE="${PLOC}/${PNAME}"; fi
  449                         if [ -r ${FILE} ]; then
  450                             PROFILES="${PROFILES} ${FILE}"
  451                             case ${PNAME} in
  452                                 "custom.prf") CUSTOM_PROFILE="${FILE}" ;;
  453                                 "default.prf") DEFAULT_PROFILE="${FILE}" ;;
  454                             esac
  455                             # Set profile directory to last match (Lynis could be both installed, and run as a separate download)
  456                             if [ "${PLOC}" = "." ]; then PROFILEDIR="${WORKDIR}"; else PROFILEDIR="${PLOC}"; fi
  457                         fi
  458                     fi
  459                 done
  460             done
  461             # Search any profiles defined with --profile
  462             for FILE in ${SEARCH_PROFILES}; do
  463                 if [ -r "${FILE}" ]; then
  464                     Debug "Found profile defined with --profile"
  465                     PROFILES="${PROFILES} ${FILE}"
  466                 else
  467                     ExitFatal "Could not find or read profile (${FILE})"
  468                 fi
  469             done
  470         fi
  471         if [ "${PROFILES}" = "" ]; then
  472             echo "${RED}Fatal error: ${WHITE}No profile defined and could not find default profile${NORMAL}"
  473             echo "Search paths used --> ${tPROFILE_TARGETS}"
  474             ExitCustom 66
  475         else
  476             PROFILES=$(echo ${PROFILES} | sed 's/^ //')
  477         fi
  478     }
  479 
  480 
  481     ################################################################################
  482     # Name        : Display()
  483     # Description : Show text on screen, with markup
  484     #
  485     # Input       : <multiple parameters, see test>
  486     # Returns     : <nothing>
  487     ################################################################################
  488 
  489     Display() {
  490         INDENT=0; TEXT=""; RESULT=""; COLOR=""; SPACES=0; SHOWDEBUG=0
  491         while [ $# -ge 1 ]; do
  492             case $1 in
  493                 --color)
  494                     shift
  495                         case $1 in
  496                           GREEN)   COLOR=$GREEN   ;;
  497                           RED)     COLOR=$RED     ;;
  498                           WHITE)   COLOR=$WHITE   ;;
  499                           YELLOW)  COLOR=$YELLOW  ;;
  500                         esac
  501                 ;;
  502                 --debug)
  503                     SHOWDEBUG=1
  504                 ;;
  505                 --indent)
  506                     shift
  507                     INDENT=$1
  508                 ;;
  509                 --result)
  510                     shift
  511                     RESULT=$1
  512                 ;;
  513                 --text)
  514                     shift
  515                     TEXT=$1
  516                 ;;
  517                 *)
  518                     echo "INVALID OPTION (Display): $1"
  519                     ExitFatal
  520                 ;;
  521             esac
  522             # Go to next parameter
  523             shift
  524         done
  525 
  526         if [ -z "${RESULT}" ]; then
  527             RESULTPART=""
  528         else
  529             if [ ${CRONJOB} -eq 0 ]; then
  530                 RESULTPART=" [ ${COLOR}${RESULT}${NORMAL} ]"
  531             else
  532                 RESULTPART=" [ ${RESULT} ]"
  533             fi
  534         fi
  535 
  536         if [ -n "${TEXT}" ]; then
  537             SHOW=0
  538             if [ ${SHOW_WARNINGS_ONLY} -eq 1 ]; then
  539                 if [ "${RESULT}" = "WARNING" ]; then SHOW=1; fi
  540             elif [ ${QUIET} -eq 0 ]; then SHOW=1
  541             fi
  542 
  543             if [ ${SHOW} -eq 1 ]; then
  544                 # Display:
  545                 # - for full shells, count with -m instead of -c, to support language locale (older busybox does not have -m)
  546                 # - wc needs LANG to deal with multi-bytes characters but LANG has been unset in include/consts
  547                 LINESIZE=$(export LC_ALL= ; export LANG="${DISPLAY_LANG}";echo "${TEXT}" | wc -m | tr -d ' ')
  548                 if [ ${SHOWDEBUG} -eq 1 ]; then DEBUGTEXT=" [${PURPLE}DEBUG${NORMAL}]"; else DEBUGTEXT=""; fi
  549                 if [ ${INDENT} -gt 0 ]; then SPACES=$((62 - INDENT - LINESIZE)); fi
  550                 if [ ${SPACES} -lt 0 ]; then SPACES=0; fi
  551                 if [ ${CRONJOB} -eq 0 ]; then
  552                     # Check if we already have already discovered a proper echo command tool. It not, set it default to 'echo'.
  553                     if [ "${ECHOCMD}" = "" ]; then ECHOCMD="echo"; fi
  554                     ${ECHOCMD} "\033[${INDENT}C${TEXT}\033[${SPACES}C${RESULTPART}${DEBUGTEXT}"
  555                 else
  556                     echo "${TEXT}${RESULTPART}"
  557                 fi
  558             fi
  559         fi
  560     }
  561 
  562 
  563     ################################################################################
  564     # Name        : DisplayError()
  565     # Description : Show error on screen
  566     #
  567     # Input       : $1 = text (string), $2 = optional exit code (integer)
  568     # Returns     : <nothing>
  569     ################################################################################
  570 
  571     DisplayError() {
  572         EXITCODE=""
  573         if [ $# -gt 1 ]; then EXITCODE=$2; fi
  574         ${ECHOCMD} ""
  575         ${ECHOCMD} "${WARNING}Error${NORMAL}: ${BOLD}$1${NORMAL}"
  576         ${ECHOCMD} ""
  577         if [ -n "${EXITCODE}" ]; then ExitCustom ${EXITCODE}; fi
  578     }
  579 
  580 
  581     ################################################################################
  582     # Name        : DisplayException()
  583     # Description : Show a discovered exception on screen
  584     #
  585     # Parameters  : $1 = function or test
  586     #               $2 = text
  587     # Returns     : <nothing>
  588     # Note        : This function is usually triggered by ReportException
  589     ################################################################################
  590 
  591     DisplayException() {
  592         ${ECHOCMD:-echo} ""
  593         ${ECHOCMD:-echo} "================================================================="
  594         ${ECHOCMD:-echo} ""
  595         ${ECHOCMD:-echo} "  ${WARNING}Exception found!${NORMAL}"
  596         ${ECHOCMD:-echo} ""
  597         ${ECHOCMD:-echo} "  Function/test:  [$1]"
  598         ${ECHOCMD:-echo} "  Message:        ${BOLD}$2${NORMAL}"
  599         ${ECHOCMD:-echo} ""
  600         ${ECHOCMD:-echo} "  Help improving the Lynis community with your feedback!"
  601         ${ECHOCMD:-echo} ""
  602         ${ECHOCMD:-echo} "  Steps:"
  603         ${ECHOCMD:-echo} "  - Ensure you are running the latest version ($0 update check)"
  604         ${ECHOCMD:-echo} "  - If so, create a GitHub issue at ${PROGRAM_SOURCE}"
  605         ${ECHOCMD:-echo} "  - Include relevant parts of the log file or configuration file"
  606         ${ECHOCMD:-echo} ""
  607         ${ECHOCMD:-echo} "  Thanks!"
  608         ${ECHOCMD:-echo} ""
  609         ${ECHOCMD:-echo} "================================================================="
  610         ${ECHOCMD:-echo} ""
  611         sleep 5
  612     }
  613 
  614 
  615     ################################################################################
  616     # Name        : DisplayManual()
  617     # Description : Show text on screen, without any markup
  618     #
  619     # Input       : $1 = text (string)
  620     # Returns     : <nothing>
  621     ################################################################################
  622 
  623     DisplayManual() {
  624         if [ ${QUIET} -eq 0 ]; then ${ECHOCMD} "$1"; fi
  625     }
  626 
  627 
  628     ################################################################################
  629     # Name        : DisplayToolTip()
  630     # Description : Show tooltip on screen
  631     #
  632     # Input       : $1 = text
  633     # Returns     : <nothing>
  634     ################################################################################
  635 
  636     DisplayToolTip() {
  637         # Display tooltip when enabled and no tip has been displayed yet
  638         if [ ${SHOW_TOOL_TIPS} -eq 1 -a ${TOOLTIP_SHOWED} -eq 0 -a ${QUIET} -eq 0 ]; then
  639             # Check if we already have already discovered a proper echo command tool. It not, set it default to 'echo'.
  640             if [ "${ECHOCMD}" = "" ]; then ECHOCMD="echo"; fi
  641             if [ ${CRONJOB} -eq 0 ]; then
  642                 printf "\n"
  643                 ${ECHOCMD} "  ${BG_BLUE}[TIP]${NORMAL}: ${LIGHTBLUE}$1${NORMAL}"
  644                 printf "\n"
  645             else
  646                 ${ECHOCMD} "  [TIP]: $1"
  647             fi
  648             TOOLTIP_SHOWED=1
  649         fi
  650     }
  651 
  652 
  653     ################################################################################
  654     # Name        : DisplayWarning
  655     # Description : Show a warning on the screen
  656     #
  657     # Parameters  : $1 = text
  658     # Returns     : <nothing>
  659     ################################################################################
  660 
  661     DisplayWarning() {
  662         if [ ${CRONJOB} -eq 0 ]; then
  663             printf "\n"
  664             ${ECHOCMD:-echo} "  ${BG_WARNING}[WARNING]${NORMAL}: $1${NORMAL}"
  665             printf "\n"
  666         else
  667             ${ECHOCMD} "  [WARNING]: $1"
  668         fi
  669     }
  670 
  671 
  672     ################################################################################
  673     # Name        : Equals()
  674     # Description : Compare two strings after special characters were stripped
  675     #
  676     # Parameters  : $1 = string1
  677     #               $2 = string2
  678     # Returns     : exit code (0 = True, 1 = False)
  679     # Usage       : if Equals "${MYDIR}" "/etc"; then echo "Found"; else "Not found"; fi
  680     ################################################################################
  681 
  682     Equals() {
  683         RETVAL=1
  684         if [ $# -ne 2 ]; then ReportException "Equals" "Incorrect number of arguments for $0 function"; fi
  685 
  686         # Strip any strange control characters
  687         INPUT1=$(echo $1 | tr -d '[:cntrl:]<>' | ${SEDBINARY} 's/__space__/ /g' | ${SEDBINARY} 's/:space:/ /g')
  688         INPUT2=$(echo $2 | tr -d '[:cntrl:]<>' | ${SEDBINARY} 's/__space__/ /g' | ${SEDBINARY} 's/:space:/ /g')
  689         if [ "${INPUT1}" = "${INPUT2}" ]; then RETVAL=0; fi
  690 
  691         return ${RETVAL}
  692     }
  693 
  694 
  695     ################################################################################
  696     # Name        : ExitClean()
  697     # Description : Perform a normal exit of the program, and clean up resources
  698     #
  699     # Parameters  : <nothing>
  700     # Returns     : <nothing>
  701     # Usage       : ExitClean
  702     ################################################################################
  703 
  704     ExitClean() {
  705         RemovePIDFile
  706         RemoveTempFiles
  707         LogText "${PROGRAM_NAME} ended successfully."
  708         exit 0
  709     }
  710 
  711 
  712     ################################################################################
  713     # Name        : ExitCustom()
  714     # Description : Perform a normal exit of the program, and clean up resources
  715     #
  716     # Parameters  : $1 = exit code (optional)
  717     # Returns     : <nothing>
  718     # Usage       : ExitCustom 35
  719     ################################################################################
  720 
  721     ExitCustom() {
  722         RemovePIDFile
  723         RemoveTempFiles
  724         # Exit with the exit code given, otherwise use 1
  725         if [ $# -eq 1 ]; then
  726             LogText "${PROGRAM_NAME} ended with exit code $1."
  727             exit $1
  728         else
  729             LogText "${PROGRAM_NAME} ended with exit code 1."
  730             exit 1
  731         fi
  732     }
  733 
  734 
  735     ################################################################################
  736     # Name        : ExitFatal()
  737     # Description : Perform exit of the program (with code 1), clean up resources
  738     #
  739     # Parameters  : $1 = text string (optional)
  740     # Returns     : <nothing>
  741     # Usage       : ExitFatal
  742     ################################################################################
  743 
  744     ExitFatal() {
  745         RemovePIDFile
  746         RemoveTempFiles
  747         LogText "${PROGRAM_NAME} ended with exit code 1."
  748         if [ $# -eq 1 ]; then
  749             ${ECHOCMD:-echo} ""
  750             ${ECHOCMD:-echo} "${RED}Fatal error${NORMAL}: ${WHITE}$1${NORMAL}"
  751             ${ECHOCMD:-echo} ""
  752         fi
  753         exit 1
  754     }
  755 
  756 
  757     ################################################################################
  758     # Name        : FileExists()
  759     # Description : Determine if a file exists
  760     #
  761     # Parameters  : $1 = path
  762     # Returns     : 0 (found), 1 (not found)
  763     #               FILE_FOUND (0:found, 1:not found) - deprecated usage
  764     ################################################################################
  765 
  766     FileExists() {
  767         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling FileExists function"; fi
  768         FILE_FOUND=0
  769         LogText "Test: checking if file $1 exists"
  770         if [ -f $1 ]; then
  771             LogText "Result: file $1 exists"
  772             FILE_FOUND=1
  773             return 0
  774         else
  775             LogText "Result: file $1 NOT found"
  776             return 1
  777         fi
  778     }
  779 
  780 
  781     ################################################################################
  782     # Name        : FileInstalledByPackage()
  783     # Description : Check if a file is part of a package
  784     # Returns     : 0 (true), 1 (default: unknown or false)
  785     ################################################################################
  786 
  787     FileInstalledByPackage() {
  788         exitcode=1
  789         file=$1
  790         find=""
  791         if [ -n "${DPKGBINARY}" ]; then
  792             find=$(${DPKGBINARY} -S "${file}" 2> /dev/null | ${AWKBINARY} -F: '{print $1}')
  793         elif [ -n "${RPMBINARY}" ]; then
  794             find=$(${RPMBINARY} -qf "${file}" 2> /dev/null | ${AWKBINARY} -F- '{print $1}')
  795         fi
  796         if [ -n "${find}" ]; then
  797             LogText "Result: file '${file}' belongs to package (${find})"
  798             exitcode=0
  799         else
  800             LogText "Result: file '${file}' does most likely not belong to a package"
  801         fi
  802         return ${exitcode}
  803     }
  804 
  805 
  806     ################################################################################
  807     # Name        : FileIsEmpty()
  808     # Description : Check if a file is empty
  809     #
  810     # Returns     : 0 (empty), 1 (not empty)
  811     #               EMPTY (0 or 1) - deprecated usage
  812     # Usage       : if FileIsEmpty /etc/passwd; then
  813     ################################################################################
  814 
  815     FileIsEmpty() {
  816         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling FileIsEmpty function"; fi
  817         EMPTY=0
  818         LogText "Test: checking if file $1 is empty"
  819         if [ ! -s "$1" ]; then
  820             LogText "Result: file $1 is empty"
  821             EMPTY=1
  822             return 0
  823         else
  824             LogText "Result: file $1 is NOT empty"
  825             return 1
  826         fi
  827     }
  828 
  829 
  830     ################################################################################
  831     # Name        : FileIsReadable()
  832     # Description : Check if a file readable or directory is accessible
  833     #
  834     # Returns     : Return code (0 = readable, 1 = not readable)
  835     # Usage       : if FileIsReadable /etc/shadow; then echo "File is readable"; fi
  836     ################################################################################
  837 
  838     FileIsReadable() {
  839         if [ $# -eq 0 ]; then ExitFatal "Function FileIsReadable() called without a file name"; fi
  840         sFILE=$1
  841         CANREAD=0
  842         RETVAL=1
  843         escaped_file=$(echo ${sFILE} | sed 's/\*/\\*/; s/?/\\?/')
  844         LogText "Test: check if we can access ${sFILE} (escaped: ${escaped_file})"
  845 
  846         # Check for symlink
  847         if [ -L "${escaped_file}" ]; then
  848             ShowSymlinkPath ${escaped_file}
  849             if [ -n "${SYMLINK}" ]; then escaped_file="${SYMLINK}"; fi
  850         fi
  851 
  852         # Only check the file if it isn't a symlink (after previous check)
  853         if [ -L "${escaped_file}" ]; then
  854             OTHERPERMS="-"
  855             LogText "Result: unclear if we can read this file, as this is a symlink"
  856             ReportException "FileIsReadable" "Can not determine symlink ${sFILE}"
  857         elif [ -d "${escaped_file}" ]; then
  858             OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2> /dev/null | ${CUTBINARY} -c 8)
  859         elif [ -f "${escaped_file}" ]; then
  860             OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2> /dev/null | ${CUTBINARY} -c 8)
  861         else
  862             OTHERPERMS="-"
  863         fi
  864 
  865         # Also check if we are the actual owner of the file (use -d to get directory itself, if its a directory)
  866         FILEOWNER=$(ls -dln "${escaped_file}" 2> /dev/null | ${AWKBINARY} -F" " '{ print $3 }')
  867         if [ "${FILEOWNER}" = "${MYID}" ]; then
  868             LogText "Result: file is owned by our current user ID (${MYID}), checking if it is readable"
  869             if [ -L "${sFILE}" ]; then
  870                 LogText "Result: unclear if we can read this file, as this is a symlink"
  871                 ReportException "FileIsReadable" "Can not determine symlink ${escaped_file}"
  872             elif [ -d "${escaped_file}" ]; then
  873                 OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2> /dev/null | ${CUTBINARY} -c 2)
  874             elif [ -f "${escaped_file}" ]; then
  875                 OTHERPERMS=$(${LSBINARY} -l "${escaped_file}" 2> /dev/null | ${CUTBINARY} -c 2)
  876             fi
  877         else
  878             LogText "Result: file is not owned by current user ID (${MYID}), but UID ${FILEOWNER}"
  879         fi
  880 
  881         # Check if we are root, or have the read bit
  882         if [ "${MYID}" = "0" -o "${OTHERPERMS}" = "r" ]; then
  883             CANREAD=1
  884             LogText "Result: file ${escaped_file} is readable (or directory accessible)."
  885             return 0
  886         else
  887             return 1
  888             LogText "Result: file ${escaped_file} is NOT readable (or directory accessible), symlink, or does not exist. (OTHERPERMS: ${OTHERPERMS})"
  889         fi
  890     }
  891 
  892 
  893     ################################################################################
  894     # Name        : GetHostID()
  895     # Description : Create an unique id for the system
  896     #
  897     # Returns     : 0 = fetched or created IDs, 1 = failed, 2 = skipped
  898     # Usage       : GetHostID
  899     ################################################################################
  900 
  901     GetHostID() {
  902         if [ ${SKIP_GETHOSTID} -eq 1 ]; then
  903             Debug "Skipping HostID generation due to SKIP_GETHOSTID"
  904             return 2
  905         fi
  906 
  907         if [ -n "${HOSTID}" -a -n "${HOSTID2}" ]; then
  908             Debug "Skipping creation of host identifiers, as they are already configured (via profile)"
  909             HOSTID_GEN="profile"
  910             return 2
  911         fi
  912 
  913         if [ -f "${ROOTDIR}etc/lynis/hostids" ]; then
  914             HOSTID=$(grep "^hostid=" ${ROOTDIR}etc/lynis/hostids | awk -F= '{print $2}')
  915             HOSTID2=$(grep "^hostid2=" ${ROOTDIR}etc/lynis/hostids | awk -F= '{print $2}')
  916             Debug "Used hostids file to fetch values"
  917             HOSTID_GEN="hostids-file"
  918             return 0
  919         fi
  920 
  921         FIND=""
  922         # Avoid some hashes (empty, only zeros)
  923         BLACKLISTED_HASHES="6ef1338f520d075957424741d7ed35ab5966ae97 adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"
  924         # Check which utilities we can use (e.g. lynis show hostids). Normally these are detected during binaries collecting.
  925         if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1sum 2> /dev/null | grep -v "no [^ ]* in "); fi
  926         if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1 2> /dev/null | grep -v "no [^ ]* in "); fi
  927         if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256sum 2> /dev/null | grep -v "no [^ ]* in "); fi
  928         if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256 2> /dev/null | grep -v "no [^ ]* in "); fi
  929         if [ "${CSUMBINARY}" = "" ]; then CSUMBINARY=$(which csum 2> /dev/null | grep -v "no [^ ]* in "); fi
  930         if [ "${OPENSSLBINARY}" = "" ]; then OPENSSLBINARY=$(which openssl 2> /dev/null | grep -v "no [^ ]* in "); fi
  931         if [ "${IFCONFIGBINARY}" = "" ]; then IFCONFIGBINARY=$(which ifconfig 2> /dev/null | grep -v "no [^ ]* in "); fi
  932         if [ "${IPBINARY}" = "" ]; then IPBINARY=$(which ip 2> /dev/null | grep -v "no [^ ]* in "); fi
  933 
  934         # If using openssl, use the best hash type it supports
  935         if [ ! "${OPENSSLBINARY}" = "" ]; then
  936             OPENSSL_HASHLIST=$(openssl dgst -h 2>&1)
  937             for OPENSSL_HASHTYPE in sha256 sha1 md5 ; do
  938                 if echo "${OPENSSL_HASHLIST}" | grep "^-${OPENSSL_HASHTYPE} " >/dev/null ; then
  939                     break
  940                 fi
  941             done
  942         fi
  943 
  944         if [ ! "${SHA1SUMBINARY}" = "" -o ! "${OPENSSLBINARY}" = "" -o ! "${CSUMBINARY}" = "" ]; then
  945             LogText "Info: found hashing tool, start generation of HostID"
  946             case "${OS}" in
  947 
  948                 "AIX")
  949                     # Common interfaces: en0 en1 en2, ent0 ent1 ent2
  950                     FIND=$(entstat en0 2>/dev/null | grep "Hardware Address" | awk -F ": " '{ print $2 }')
  951                     if [ "${FIND}" = "" ]; then
  952                         FIND=$(entstat ent0 2>/dev/null | grep "Hardware Address" | awk -F ": " '{ print $2 }')
  953                     fi
  954                     if [ ! "${FIND}" = "" ]; then
  955                         # We have a MAC address, now hashing it
  956                         if [ -n "${SHA1SUMBINARY}" ]; then
  957                             HOSTID=$(echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }')
  958                         elif [ -n "${CSUMBINARY}" ]; then
  959                             HOSTID=$(echo ${FIND} | ${CSUMBINARY} -h SHA1 - | awk '{ print $1 }')
  960                         elif [ -n "${OPENSSLBINARY}" ]; then
  961                             HOSTID=$(echo ${FIND} | ${OPENSSLBINARY} sha -sha1 | awk '{ print $2 }')
  962                         else
  963                             ReportException "GetHostID" "No sha1, sha1sum, csum or openssl binary available on AIX"
  964                         fi
  965                     else
  966                         ReportException "GetHostID" "No output from entstat on interfaces: en0, ent0"
  967                     fi
  968                 ;;
  969 
  970                 "DragonFly" | "FreeBSD")
  971                     FIND=$(${IFCONFIGBINARY} | grep ether | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]')
  972                     if HasData "${FIND}"; then
  973                         HOSTID=$(echo ${FIND} | sha1)
  974                     else
  975                         ReportException "GetHostID" "No MAC address returned on DragonFly or FreeBSD"
  976                     fi
  977                 ;;
  978 
  979                 "HP-UX")
  980                     FIND=$(nwmgr -q info -c lan0 2> /dev/null | awk '{ if ($1=="MAC" && $2=="Address") { print $4 }}')
  981                     if HasData "${FIND}"; then
  982                         if [ -n "${OPENSSLBINARY}" ]; then
  983                             HOSTID=$(echo ${FIND} | ${OPENSSLBINARY} sha -sha1 | awk '{ print $2 }')
  984                         else
  985                             ReportException "GetHostID" "No openssl binary available on this HP-UX system"
  986                         fi
  987                     else
  988                         ReportException "GetHostID" "No MAC address found by using nwmgr"
  989                     fi
  990                 ;;
  991 
  992                 "Linux")
  993                     # Try fetching information from /sys in case 'ip' is not available or does not give expected results
  994                     if IsEmpty "${FIND}" && [ -d /sys/class/net ]; then
  995                         NET_INTERFACES=$(${FINDBINARY} /sys/class/net ! -type d -exec realpath {} \; 2> /dev/null | sort | awk -F'/' '!/virtual/ && /devices/ {for (x=1;x<=NF;x++) if ($x~"net") print $(x+1)}')
  996                         for INTERFACE in ${NET_INTERFACES}; do
  997                             if grep -q -s 'up' "/sys/class/net/${INTERFACE}/operstate"; then
  998                                 LogText "Interface '${INTERFACE}' is up, fetching MAC address"
  999                                 FIND=$(head -1 "/sys/class/net/${INTERFACE}/address" | tr '[:upper:]' '[:lower:]')
 1000                                 if HasData "${FIND}"; then
 1001                                     HOSTID_GEN="linux-sys-interface-up"
 1002                                     break
 1003                                 fi
 1004                             fi
 1005                         done
 1006                     fi
 1007 
 1008                     # Next is to try ip, as it is available to most modern Linux distributions
 1009                     if IsEmpty "${FIND}" && [ -n "${IPBINARY}" ]; then
 1010                         LogText "Info: trying output from 'ip' to generate HostID"
 1011                         # Determine if we have the common available eth0 interface. If so, give that priority.
 1012                         # Note: apply sorting in case there would be multiple MAC addresses linked to increase predictable end result
 1013                         FIND=$(${IPBINARY} addr show eth0 2> /dev/null | grep -E "link/ether " | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]' | sort | head -1)
 1014                         if HasData "${FIND}"; then
 1015                             HOSTID_GEN="linux-ip-interface-eth0"
 1016                         else
 1017                             # If eth0 does not exist, which is also common, then trying the next option:
 1018                             # 1) First fetch all links that are UP
 1019                             # 2) Filter entries that have a MAC address and filter out Docker related MAC addresses starting with '02:42:'
 1020                             # 3) Convert everything to lowercase
 1021                             # 4) Sort the entries, so that the output is more predictable between runs when the same interfaces are available
 1022                             # 5) Select first entry
 1023                             FIND=$(${IPBINARY} -family link addr show up 2> /dev/null | awk '{if($1=="link/ether" && $2 !~ "^02:42:"){print $2}}' | tr '[:upper:]' '[:lower:]' | sort | head -1)
 1024                             if HasData "${FIND}"; then
 1025                                 HOSTID_GEN="linux-ip-interface-up-other"
 1026                             else
 1027                                 ReportException "GetHostID" "Can't create hostid (no MAC addresses found)"
 1028                             fi
 1029                         fi
 1030                     fi
 1031 
 1032                     # Finally try ifconfig
 1033                     if IsEmpty "${FIND}" && [ -n "${IFCONFIGBINARY}" ]; then
 1034                         LogText "Info: no information found from 'ip' or in /sys, trying output from 'ifconfig'"
 1035                         # Determine if we have the eth0 interface (not all Linux distributions have this, e.g. Arch)
 1036                         HASETH0=$(${IFCONFIGBINARY} | grep "^eth0")
 1037                         # Check if we can find it with HWaddr on the line
 1038                         FIND=$(${IFCONFIGBINARY} 2> /dev/null | grep "^eth0" | grep -v "eth0:" | grep HWaddr | awk '{ print $5 }' | tr '[:upper:]' '[:lower:]')
 1039 
 1040                         # If nothing found, then try first for alternative interface. Else other versions of ifconfig (e.g. Slackware/Arch)
 1041                         if IsEmpty "${FIND}"; then
 1042                             FIND=$(${IFCONFIGBINARY} 2> /dev/null | grep HWaddr)
 1043                             if IsEmpty "${FIND}"; then
 1044                                 # If possible directly address eth0 to avoid risking gathering the incorrect MAC address.
 1045                                 # If not, then falling back to getting first interface. Better than nothing.
 1046                                 if HasData "${HASETH0}"; then
 1047                                     FIND=$(${IFCONFIGBINARY} eth0 2> /dev/null | grep "ether " | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]')
 1048                                     if HasData "${FIND}"; then
 1049                                         HOSTID_GEN="linux-ifconfig-interface-eth0-ether"
 1050                                     fi
 1051                                 else
 1052                                     FIND=$(${IFCONFIGBINARY} 2> /dev/null | grep "ether " | awk '{ print $2 }' | head -1 | tr '[:upper:]' '[:lower:]')
 1053                                     if IsEmpty "${FIND}"; then
 1054                                         ReportException "GetHostID" "No eth0 found (and no ether was found with ifconfig)"
 1055                                     else
 1056                                         HOSTID_GEN="linux-ifconfig-interface-first-ether"
 1057                                         LogText "Result: No eth0 found (but ether found), using first network interface to determine hostid (with ifconfig)"
 1058                                     fi
 1059                                 fi
 1060                             else
 1061                                 FIND=$(${IFCONFIGBINARY} 2> /dev/null | grep HWaddr | head -1 | awk '{ print $5 }' | tr '[:upper:]' '[:lower:]')
 1062                                 HOSTID_GEN="linux-ifconfig-interface-first-hwaddr"
 1063                             fi
 1064                         else
 1065                             HOSTID_GEN="linux-ifconfig-interface-eth0-hwaddr"
 1066                         fi
 1067                     fi
 1068 
 1069                     # Check if we found a MAC address to generate the HostID
 1070                     if HasData "${FIND}"; then
 1071                         LogText "Info: using hardware address '${FIND}' to create HostID"
 1072                         HOSTID=$(echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }')
 1073                         LogText "Result: Found HostID: ${HOSTID}"
 1074                     else
 1075                         ReportException "GetHostID" "HostID could not be generated"
 1076                     fi
 1077                 ;;
 1078 
 1079                 "macOS")
 1080                     FIND=$(${IFCONFIGBINARY} en0 | grep ether | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]')
 1081                     if [ ! "${FIND}" = "" ]; then
 1082                         HOSTID=$(echo ${FIND} | shasum | awk '{ print $1 }')
 1083                     else
 1084                         ReportException "GetHostID" "No MAC address returned on macOS"
 1085                     fi
 1086                     LYNIS_HOSTID2_PART1=$(hostname -s)
 1087                     if [ -n "${LYNIS_HOSTID2_PART1}" ]; then
 1088                         LogText "Info: using hostname ${LYNIS_HOSTID2_PART1}"
 1089                         LYNIS_HOSTID2_PART2=$(sysctl -n kern.uuid 2> /dev/null)
 1090                         if [ -n "${LYNIS_HOSTID2_PART2}" ]; then
 1091                             LogText "Info: using UUID ${LYNIS_HOSTID2_PART2}"
 1092                         else
 1093                             LogText "Info: could not create HOSTID2 as kern.uuid sysctl key is missing"
 1094                         fi
 1095                         HOSTID2=$(echo "${LYNIS_HOSTID2_PART1}${LYNIS_HOSTID2_PART2}" | shasum -a 256 | awk '{ print $1 }')
 1096                     else
 1097                         LogText "Info: could not create HOSTID2 as hostname is missing"
 1098                     fi
 1099                 ;;
 1100 
 1101                 "NetBSD")
 1102                     FIND=$(${IFCONFIGBINARY} -a | grep "address:" | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]')
 1103                     if HasData "${FIND}"; then
 1104                         HOSTID=$(echo ${FIND} | sha1)
 1105                     else
 1106                         ReportException "GetHostID" "No MAC address returned on NetBSD"
 1107                     fi
 1108                 ;;
 1109 
 1110                 "OpenBSD")
 1111                     FIND=$(${IFCONFIGBINARY} | grep "lladdr " | head -1 | awk '{ print $2 }' | tr '[:upper:]' '[:lower:]')
 1112                     if HasData "${FIND}"; then
 1113                         HOSTID=$(echo ${FIND} | sha1)
 1114                     else
 1115                         ReportException "GetHostID" "No MAC address returned on OpenBSD"
 1116                     fi
 1117                 ;;
 1118 
 1119                 "Solaris")
 1120                     INTERFACES_TO_TEST="net0 e1000g1 e1000g0"
 1121                     FOUND=0
 1122                     for I in ${INTERFACES_TO_TEST}; do
 1123                          FIND=$(${IFCONFIGBINARY} -a | grep "^${I}")
 1124                          if [ ! "${FIND}" = "" ]; then
 1125                              FOUND=1; LogText "Found interface ${I} on Solaris"
 1126                              break
 1127                          fi
 1128                     done
 1129                     if [ ${FOUND} -eq 1 ]; then
 1130                         FIND=$(${IFCONFIGBINARY} ${I} | grep ether | awk '{ if ($1=="ether") { print $2 }}')
 1131                         if [ -n "${SHA1SUMBINARY}" ]; then
 1132                             HOSTID=$(echo ${FIND} | ${SHA1SUMBINARY} | awk '{ print $1 }')
 1133                         elif [ -n "${OPENSSLBINARY}" ]; then
 1134                             HOSTID=$(echo ${FIND} | ${OPENSSLBINARY} sha -sha1 | awk '{ print $2 }')
 1135                         else
 1136                             ReportException "GetHostID" "Can not find sha1/sha1sum or openssl"
 1137                         fi
 1138                     else
 1139                         ReportException "GetHostID" "No interface found on Solaris to create HostID"
 1140                     fi
 1141                 ;;
 1142 
 1143                 *)
 1144                         ReportException "GetHostID" "Can't create HOSTID as OS is not supported yet by this function"
 1145                 ;;
 1146             esac
 1147 
 1148             # Remove HOSTID if it contains a default MAC address with a related hash value
 1149             if [ -n "${HOSTID}" ]; then
 1150                 for CHECKHASH in ${BLACKLISTED_HASHES}; do
 1151                     if [ "${CHECKHASH}" = "${HOSTID}" ]; then
 1152                         LogText "Result: hostid is a blacklisted value"
 1153                         HOSTID=""
 1154                     fi
 1155                 done
 1156             fi
 1157 
 1158         else
 1159             ReportException "GetHostID" "Can't create HOSTID as there is no SHA1 hash tool available (sha1, sha1sum, openssl)"
 1160         fi
 1161 
 1162         # Search machine ID
 1163         # This applies to IDs generated for systemd
 1164         # 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)
 1165         sMACHINEIDFILE="/etc/machine-id"
 1166         if [ -f ${sMACHINEIDFILE} ]; then
 1167             FIND=$(head -1 ${sMACHINEIDFILE} | grep "^[a-f0-9]")
 1168             if [ "${FIND}" = "" ]; then
 1169                 MACHINEID="${FIND}"
 1170             fi
 1171         fi
 1172 
 1173         if [ -z "${HOSTID}" ]; then
 1174             LogText "Result: no HOSTID available, trying to use SSH key as unique source"
 1175             # Create host ID when a MAC address was not found
 1176             SSH_KEY_FILES="ssh_host_ed25519_key.pub ssh_host_ecdsa_key.pub ssh_host_dsa_key.pub ssh_host_rsa_key.pub"
 1177             if [ -d /etc/ssh ]; then
 1178                 for I in ${SSH_KEY_FILES}; do
 1179                     if [ -z "${HOSTID}" ]; then
 1180                         if [ -f /etc/ssh/${I} ]; then
 1181                             LogText "Result: found ${I} in /etc/ssh"
 1182                             if [ -n "${SHA1SUMBINARY}" ]; then
 1183                                 HOSTID=$(${SHA1SUMBINARY} /etc/ssh/${I} | awk '{ print $1 }')
 1184                                 LogText "result: Created HostID with SSH key ($I): ${HOSTID}"
 1185                                 HOSTID_GEN="fallback-ssh-public-key"
 1186                             else
 1187                                 ReportException "GetHostID" "Can't create HOSTID with SSH key, as sha1sum binary is missing"
 1188                             fi
 1189                         fi
 1190                     fi
 1191                 done
 1192             else
 1193                 LogText "Result: no /etc/ssh directory found, skipping"
 1194             fi
 1195         fi
 1196 
 1197         # Generation of HostID version 2
 1198         if [ -z "${HOSTID2}" ]; then
 1199             LogText "Info: start generation of HostID (version 2)"
 1200             FOUND=0
 1201             DATA_SSH=""
 1202             # Use public keys
 1203             SSH_KEY_FILES="ssh_host_ed25519_key.pub ssh_host_ecdsa_key.pub ssh_host_dsa_key.pub ssh_host_rsa_key.pub"
 1204             if [ -d /etc/ssh ]; then
 1205                 for I in ${SSH_KEY_FILES}; do
 1206                     if [ ${FOUND} -eq 0 ]; then
 1207                         if [ -f /etc/ssh/${I} ]; then
 1208                             LogText "Result: found file ${I} in /etc/ssh, using that as candidate to create hostid2"
 1209                             DATA_SSH=$(cat /etc/ssh/${I})
 1210                             FOUND=1
 1211                         fi
 1212                     fi
 1213                 done
 1214             else
 1215                 LogText "Result: no /etc/ssh directory found, skipping"
 1216             fi
 1217 
 1218             STRING_TO_HASH=""
 1219             if [ ${FOUND} -eq 1 -a -n "${DATA_SSH}" ]; then
 1220                 LogText "Using SSH public key to create hostid2"
 1221                 STRING_TO_HASH="${DATA_SSH}"
 1222                 HOSTID2_GEN="ssh-public-key"
 1223             else
 1224                 if [ -n "${MACHINEID}" ]; then
 1225                     LogText "Using the machine ID to create hostid2"
 1226                     STRING_TO_HASH="${MACHINEID}"
 1227                     HOSTID2_GEN="machine-id"
 1228                 fi
 1229             fi
 1230             # Check if we have a string to turn into a host identifier
 1231             if [ -n "${STRING_TO_HASH}" ]; then
 1232                 # Create hashes
 1233                 if [ -n "${SHA256SUMBINARY}" ]; then
 1234                     HASH2=$(echo ${STRING_TO_HASH} | ${SHA256SUMBINARY} | awk '{ print $1 }')
 1235                     HASH_HOSTNAME=$(echo ${HOSTNAME} | ${SHA256SUMBINARY} | awk '{ print $1 }')
 1236                 elif [ -n "${OPENSSLBINARY}" ]; then
 1237                     HASH2=$(echo ${STRING_TO_HASH} | ${OPENSSLBINARY} dgst -${OPENSSL_HASHTYPE} | awk '{ print $2 }')
 1238                     HASH_HOSTNAME=$(echo ${HOSTNAME} | ${OPENSSLBINARY} dgst -${OPENSSL_HASHTYPE} | awk '{ print $2 }')
 1239                 fi
 1240                 LogText "Hash (hostname): ${HASH_HOSTNAME}"
 1241                 LogText "Hash (ssh or machineid): ${HASH2}"
 1242                 HOSTID2="${HASH2}"
 1243             fi
 1244         fi
 1245 
 1246         # Show an exception if no HostID could be created, to ensure each system (and scan) has one
 1247         if [ -z "${HOSTID}" ]; then
 1248             ReportException "GetHostID" "No unique host identifier could be created."
 1249             return 1
 1250         elif [ -n "${HOSTID2}" ]; then
 1251             return 0
 1252         fi
 1253     }
 1254 
 1255 
 1256     ################################################################################
 1257     # Name        : GetReportData()
 1258     # Description : Request data from report
 1259     # Returns     : Data (when matches were found)
 1260     # Returns     : exit code (0 = True, 1 = False, meaning search was cancelled)
 1261     #               stdout (output of search result)
 1262     ################################################################################
 1263 
 1264     GetReportData() {
 1265         KEY=""
 1266         VALID_CHARS="[:alnum:]/:;\-,\._\[\]\n "
 1267         if [ $# -eq 0 ]; then ExitFatal "No parameters provided to GetReportData() function"; fi
 1268 
 1269         while [ $# -ge 1 ]; do
 1270             case $1 in
 1271                 --key)
 1272                     shift
 1273                     KEY="$1"
 1274                 ;;
 1275                 --valid-chars)
 1276                     shift
 1277                     VALID_CHARS="$1"
 1278                 ;;
 1279                 *)
 1280                     ExitFatal "Invalid option provided to GetReportData() function"
 1281                 ;;
 1282             esac
 1283             # Go to next parameter
 1284             shift
 1285         done
 1286 
 1287         if [ "${REPORTFILE}" = "/dev/null" ]; then
 1288             return 1
 1289         else
 1290             ${AWKBINARY} -v pattern="^${KEY}" -F= '$1 ~ pattern {print $2}' ${REPORTFILE} | ${TRBINARY} -cd "${VALID_CHARS}" | ${TRBINARY} '[:blank:]' '__space__'
 1291         fi
 1292         return 0
 1293     }
 1294 
 1295 
 1296     ################################################################################
 1297     # Name        : HasCorrectFilePermissions()
 1298     # Description : Check file permissions
 1299     #
 1300     # Parameters  : $1 = Full path to file or directory
 1301     #               $2 = Permissions
 1302     # Returns     : exit code (0 = correct, 1 = not correct, 2 = file does not exist)
 1303     ################################################################################
 1304 
 1305     HasCorrectFilePermissions() {
 1306         if [ $# -ne 2 ]; then Fatal "Incorrect usage of HasCorrectFilePermissions"; fi
 1307         CHECKFILE="$1"
 1308         CHECKPERMISSION_FULL="$2"
 1309         if [ ! -d ${CHECKFILE} -a ! -f ${CHECKFILE} ]; then
 1310             return 2
 1311         else
 1312             for CHECK_PERMISSION in ${CHECKPERMISSION_FULL}; do
 1313                 DATA=$(echo ${CHECK_PERMISSION} | ${EGREPBINARY} "[rwx]")
 1314                 if [ $? -eq 0 ]; then
 1315                     # add a dummy character as first character so it looks like output is a normal file
 1316                     CHECK_PERMISSION=$(echo "-${CHECK_PERMISSION}" | ${AWKBINARY} '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf("%0o",k)}')
 1317                 fi
 1318 
 1319                 # Add leading zeros if necessary
 1320                 CHECK_PERMISSION=$(echo "${CHECK_PERMISSION}" | ${AWKBINARY} '{printf "%03d",$1}')
 1321 
 1322                 # First try stat command
 1323                 LogText "Test: checking if file ${CHECKFILE} has the permissions set to ${CHECK_PERMISSION} or more restrictive"
 1324                 if [ -n "${STATBINARY}" ]; then
 1325 
 1326                     case ${OS} in
 1327                         *BSD | "macOS")
 1328                             # BSD and macOS have no --format, only short notation
 1329                             DATA=$(${STATBINARY} -f "%OLp" ${CHECKFILE})
 1330                         ;;
 1331                         *)
 1332                             # busybox does not support format
 1333                             if [ ${SHELL_IS_BUSYBOX} -eq 0 ]; then
 1334                                 DATA=$(${STATBINARY} --format=%a ${CHECKFILE})
 1335                             fi
 1336                         ;;
 1337                     esac
 1338                 fi
 1339 
 1340                 # See if we can use the find binary
 1341                 if [ -z "${DATA}" ]; then
 1342                     case ${OS} in
 1343                         "AIX" | *BSD)
 1344                             Debug "Skipping find command, as this operating system does not support -printf parameter"
 1345                         ;;
 1346                         *)
 1347                             # Only use find when OS is NOT AIX and binaries are NOT busybox
 1348                             if [ ${SHELL_IS_BUSYBOX} -eq 0 ]; then
 1349                                 if [ -d ${CHECKFILE} ]; then
 1350                                     DATA=$(${FINDBINARY} ${CHECKFILE} -maxdepth 0 -printf "%m")
 1351                                 else
 1352                                     DATA=$(${FINDBINARY} ${CHECKFILE} -printf "%m")
 1353                                 fi
 1354                             fi
 1355                         ;;
 1356                     esac
 1357                 fi
 1358 
 1359                 # Finally use ls command
 1360                 if [ -z "${DATA}" ]; then
 1361                     # If 'file' is an directory, use -d
 1362                     if [ -d ${CHECKFILE} ]; then
 1363                         DATA=$(${LSBINARY} -d -l ${CHECKFILE} | cut -c 2-10)
 1364                     else
 1365                         DATA=$(${LSBINARY} -l ${CHECKFILE} | cut -c 2-10)
 1366                     fi
 1367                 fi
 1368 
 1369                 # Convert permissions to octal when needed
 1370                 case ${DATA} in
 1371                     [-r][-w][-x][-r][-w][-x][-r][-w][-x] )
 1372                         LogText "Converting value ${DATA} to octal"
 1373                         # add a dummy character as first character so it looks like output is a normal file
 1374                         DATA=$(echo "-${DATA}" | ${AWKBINARY} '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf("%0o",k)}')
 1375                     ;;
 1376                 esac
 1377 
 1378                 # Add leading zeros if necessary
 1379                 DATA=$(echo "${DATA}" | ${AWKBINARY} '{printf "%03d",$1}')
 1380 
 1381                 if [ -n "${DATA}" ]; then
 1382                     if [ "${DATA}" -le "${CHECK_PERMISSION}" ]; then
 1383                         LogText "Outcome: correct permissions (${DATA})"
 1384                         return 0
 1385                     fi
 1386                 else
 1387                     ReportException "HasCorrectFilePermissions:02" "No data value found, which is unexpected"
 1388                 fi
 1389             done
 1390 
 1391             LogText "Outcome: permissions of file ${CHECKFILE} are not matching expected value (${DATA} != ${CHECKPERMISSION_FULL})"
 1392             # No match, return exit code 1
 1393             return 1
 1394         fi
 1395     }
 1396 
 1397 
 1398     ################################################################################
 1399     # Name        : HasData()
 1400     # Description : Check for a filled variable
 1401     #
 1402     # Returns     : exit code (0 = True, 1 = False)
 1403     # Usage       : if HasData "${FIND}"; then
 1404     ################################################################################
 1405 
 1406     HasData() {
 1407         if [ $# -eq 1 ]; then
 1408             if [ -n "$1" ]; then return 0; else return 1; fi
 1409         else
 1410             ExitFatal "Function HasData called without parameters - look in log to determine where this happened, or use sh -x lynis to see all details."
 1411         fi
 1412     }
 1413 
 1414 
 1415     ################################################################################
 1416     # Name        : InsertSection()
 1417     # Description : Show a section block on screen
 1418     #
 1419     # Returns     : <nothing>
 1420     # Usage       : InsertSection
 1421     ################################################################################
 1422 
 1423     InsertSection() {
 1424         if [ ${QUIET} -eq 0 ]; then
 1425             echo ""
 1426             echo "[+] ${SECTION}$1${NORMAL}"
 1427             echo "------------------------------------"
 1428         fi
 1429         LogTextBreak
 1430         LogText "Action: Performing tests from category: $1"
 1431     }
 1432 
 1433 
 1434     ################################################################################
 1435     # Name        : InsertPlugionSection()
 1436     # Description : Insert section block for plugins (different color)
 1437     #
 1438     # Returns     : <nothing>
 1439     # Usage       : InsertPluginSection
 1440     ################################################################################
 1441 
 1442     InsertPluginSection() {
 1443         if [ ${QUIET} -eq 0 ]; then
 1444             echo ""
 1445             echo "[+] ${MAGENTA}$1${NORMAL}"
 1446             echo "------------------------------------"
 1447         fi
 1448         LogText "Action: Performing plugin tests"
 1449     }
 1450 
 1451 
 1452     ################################################################################
 1453     # Name        : IsContainer()
 1454     # Description : Determine if we are running in a container
 1455     #
 1456     # Parameters  : <none>
 1457     # Returns     : exit code (0 = true, 1 = false)
 1458     #               variable: CONTAINER_TYPE
 1459     ################################################################################
 1460 
 1461     IsContainer() {
 1462         FOUND=0
 1463         # Early on we can't use FileIsReadable yet
 1464         if [ -e /proc/1/cgroup ]; then
 1465             FIND=$(grep -i docker ${ROOTDIR}proc/1/cgroup 2> /dev/null)
 1466             if [ $? -eq 0 ]; then
 1467                 LogText "Result: found Docker in control groups (/proc/1/cgroup), so we are running in Docker container"
 1468                 CONTAINER_TYPE="Docker"; FOUND=1
 1469                 EXITCODE=0
 1470             fi
 1471         fi
 1472         if [ -e /proc/1/environ ]; then
 1473             FIND=$(grep -qa 'container=lxc' ${ROOTDIR}proc/1/environ 2> /dev/null)
 1474             if [ $? -eq 0 ]; then
 1475                 LogText "Result: found LXC in environment (/proc/1/environ), so we are running in LXC container"
 1476                 CONTAINER_TYPE="LXC"; FOUND=1
 1477                 EXITCODE=0
 1478             fi
 1479         fi
 1480         if [ ${FOUND} -eq 0 ]; then
 1481             CONTAINER_TYPE=""
 1482             EXITCODE=1
 1483         fi
 1484         return ${EXITCODE}
 1485     }
 1486 
 1487 
 1488     ################################################################################
 1489     # Name        : IsDebug()
 1490     # Description : Check if --debug option is used to show more details
 1491     #
 1492     # Parameters  : <none>
 1493     # Returns     : exit code (0 = True, 1 = False)
 1494     ################################################################################
 1495 
 1496     IsDebug() {
 1497         if [ ${DEBUG} -eq 1 ]; then return 0; else return 1; fi
 1498     }
 1499 
 1500 
 1501     ################################################################################
 1502     # Name        : IsDeveloperMode()
 1503     # Description : Check if we are in development mode (--developer)
 1504     #
 1505     # Parameters  : <none>
 1506     # Returns     : exit code (0 = True, 1 = False)
 1507     # Notes       : This is set with command line option or as a profile setting
 1508     ################################################################################
 1509 
 1510     IsDeveloperMode() {
 1511         if [ ${DEVELOPER_MODE} -eq 1 ]; then return 0; else return 1; fi
 1512     }
 1513 
 1514 
 1515     ################################################################################
 1516     # Name        : IsDeveloperVersion()
 1517     # Description : Check if this version is development or stable release
 1518     #
 1519     # Parameters  : <none>
 1520     # Returns     : exit code (0 = True, 1 = False)
 1521     ################################################################################
 1522 
 1523     IsDeveloperVersion() {
 1524         if [ "${PROGRAM_RELEASE_TYPE}" = "pre-release" ]; then return 0; else return 1; fi
 1525     }
 1526 
 1527 
 1528     ################################################################################
 1529     # Name        : IsEmpty()
 1530     # Description : Check for variable that has no data in it
 1531     #
 1532     # Returns     : exit code (0 = True, 1 = False)
 1533     # Usage       : if IsEmpty "${FIND}"; then
 1534     ################################################################################
 1535 
 1536     IsEmpty() {
 1537         if [ $# -eq 0 ]; then
 1538             ExitFatal "Function IsEmpty called without parameters - look in log to determine where this happened, or use sh -x lynis to see all details."
 1539         else
 1540             if [ -z "$1" ]; then return 0; else return 1; fi
 1541         fi
 1542     }
 1543 
 1544 
 1545     ################################################################################
 1546     # Name        : IsRunning()
 1547     # Description : Check if a process is running
 1548     #
 1549     # Parameters  : $1 = search argument
 1550     #               $2 = optional arguments
 1551     # Returns     : 0 (process is running), 1 (process not running)
 1552     #               RUNNING (1 = running, 0 = not running) - will be deprecated
 1553     # Notes       : PSOPTIONS are declared globally, to prevent testing each call
 1554     #               Fallback is used on binaries as IsRunning is used for 'show' command
 1555     ################################################################################
 1556 
 1557     IsRunning() {
 1558         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling IsRunning function"; fi
 1559         pgrep_options="-x"
 1560         search=""
 1561         FIND=""
 1562         PSOPTIONS=""
 1563         PARTIAL_SEARCH=1
 1564 
 1565         while [ $# -ge 1 ]; do
 1566             case $1 in
 1567                 --full)
 1568                     pgrep_options="-f" # replace -x with -f
 1569                     PARTIAL_SEARCH=0
 1570                 ;;
 1571                 --user)
 1572                     shift
 1573                     users="$1"
 1574                 ;;
 1575                 *)
 1576                     search="$1"
 1577                 ;;
 1578             esac
 1579             shift  # Go to next parameter
 1580         done
 1581 
 1582         if [ -z "${search}" ]; then ExitFatal "Missing process to search for when using IsRunning function"; fi
 1583         RUNNING=0
 1584         if [ -x "${PGREPBINARY}" ] && [ "${OS}" != "AIX" ]; then
 1585             # When --user is used, perform a search using the -u option
 1586             # Initialize users for strict mode
 1587             if [ -n "${users:-}" ]; then
 1588                 for u in ${users}; do
 1589                     user_uid=$(getent passwd "${u}" 2> /dev/null | ${AWKBINARY:-awk} -F: '{print $3}')
 1590                     # Only perform search if user exists and we had no match yet
 1591                     if [ -n "${user_uid}" ]; then
 1592                         if [ -z "${FIND}" ]; then
 1593                             LogText "Performing pgrep scan using uid ${user_uid}"
 1594                             FIND=$(${PGREPBINARY:-pgrep} ${pgrep_options} -u "${user_uid}" "${search}" | ${TRBINARY:-tr} '\n' ' ')
 1595                         fi
 1596                     fi
 1597                 done
 1598             else
 1599                 LogText "Performing pgrep scan without uid"
 1600                 FIND=$(${PGREPBINARY:-pgrep} ${pgrep_options} "${search}" | ${TRBINARY:-tr} '\n' ' ')
 1601             fi
 1602         else
 1603             if [ "${SHELL_IS_BUSYBOX}" -eq 1 ]; then
 1604                 # This search is not foolproof
 1605                 LogText "Performing simple ps scan (busybox)"
 1606                 PSOPTIONS=" -o args="
 1607                 FIND=$(${PSBINARY:-ps} ${PSOPTIONS} | ${EGREPBINARY:-egrep} "( |/)${search}" | ${GREPBINARY:-grep} -v "grep")
 1608             else
 1609                 if [ -n "${users}" ]; then
 1610                     for u in ${users}; do
 1611                         user_uid=$(getent passwd "${u}" 2> /dev/null | ${AWKBINARY:-awk} -F: '{print $3}')
 1612                         # Only perform search if user exists and we had no match yet
 1613                         if [ -n "${user_uid}" ]; then
 1614                             if [ -z "${FIND}" ]; then
 1615                                 if [ ${PARTIAL_SEARCH} -eq 1 ]; then
 1616                                     LogText "Performing ps scan using partial match and for uid ${user_uid}"
 1617                                     FIND=$(${PSBINARY:-ps} -u "${user_uid}" -o comm= "${search}" | ${AWKBINARY:-awk} -v pattern="${search}" '$0 ~ pattern {print}')
 1618                                 else
 1619                                     LogText "Performing ps scan using exact match and for uid ${user_uid}"
 1620                                     FIND=$(${PSBINARY:-ps} -u "${user_uid}" -o comm= "${search}" | ${AWKBINARY:-awk} -v pattern="^${search}$" '$0 ~ pattern {print}')
 1621                                 fi
 1622                             fi
 1623                         fi
 1624                     done
 1625                 else
 1626                     case "${OS}" in
 1627                         "Linux")
 1628                             PSOPTIONS=" -o args= -C ${search}"
 1629                         ;;
 1630                     esac
 1631                     if [ ${PARTIAL_SEARCH} -eq 1 ]; then
 1632                         LogText "Performing ps scan using partial match and without uid"
 1633                         FIND=$(${PSBINARY:-ps} ${PSOPTIONS} | ${AWKBINARY:-awk} -v pattern="${search}" '$0 ~ pattern {print}')
 1634                     else
 1635                         LogText "Performing ps scan using exact match and without uid"
 1636                         FIND=$(${PSBINARY:-ps} ${PSOPTIONS} | ${AWKBINARY:-awk} -v pattern="^${search}$" '$0 ~ pattern {print}')
 1637                     fi
 1638                 fi
 1639             fi
 1640         fi
 1641 
 1642         if [ -n "${FIND}" ]; then
 1643             RUNNING=1
 1644             LogText "IsRunning: process '${search}' found (${FIND})"
 1645             return 0
 1646         else
 1647             LogText "IsRunning: process '${search}' not found"
 1648             return 1
 1649         fi
 1650     }
 1651 
 1652 
 1653     ################################################################################
 1654     # Name        : IsNotebook
 1655     # Description : Check if file or directory is owned by root
 1656     # Returns     : exit code (0 = True, 1 = False, 255 = Unknown)
 1657     ################################################################################
 1658 
 1659     IsNotebook() {
 1660         FIND=$(which laptop-detect 2> /dev/null | grep -v "no [^ ]* in ")
 1661         if [ -n "${FIND}" ]; then
 1662             Debug "Testing if we are a notebook"
 1663             laptop-detect
 1664             if [ $? -eq 0 ]; then SYSTEM_IS_NOTEBOOK=1; Debug "System is a notebook according to laptop-detect"
 1665             elif [ $? -eq 1 ]; then SYSTEM_IS_NOTEBOOK=0; Debug "System is a NOT a notebook according to laptop-detect"; fi
 1666             Report "notebook=${SYSTEM_IS_NOTEBOOK}"
 1667         fi
 1668     }
 1669 
 1670 
 1671     ################################################################################
 1672     # Name        : IsOwnedByRoot
 1673     # Description : Check if file or directory is owned by root
 1674     # Returns     : 0 (true), 1 (false), or 255 (unknown)
 1675     ################################################################################
 1676 
 1677     IsOwnedByRoot() {
 1678         PERMS=""
 1679         if [ $# -eq 1 ]; then
 1680             FILE="$1"
 1681             case $OS in
 1682                 "AIX")
 1683                     if [ ! "${ISTATBINARY}" = "" ]; then PERMS=$(${ISTATBINARY} ${FILE} | sed "s/Owner: //" | sed "s/[a-zA-Z() ]//g"); fi
 1684                 ;;
 1685                 "Linux")
 1686                     if [ ! "${STATBINARY}" = "" ]; then PERMS=$(${STATBINARY} -c "%u:%g" ${FILE}); fi
 1687                 ;;
 1688                 "FreeBSD")
 1689                     if [ ! "${STATBINARY}" = "" ]; then PERMS=$(${STATBINARY} -f "%u:%g" ${FILE}); fi
 1690                 ;;
 1691             esac
 1692             # Fallback with ls (for other platforms, or when a test did not reveal any output)
 1693             if [ "${PERMS}" = "" ]; then
 1694                 PERMS=$(ls -n ${FILE} | ${AWKBINARY} '{ print $3":"$4 }')
 1695             fi
 1696         else
 1697             ReportException "IsOwnedByRoot" "Functions needs 1 argument"
 1698             return 255
 1699         fi
 1700         if [ "${PERMS}" = "0:0" ]; then
 1701             if IsDeveloperMode; then LogText "Debug: found incorrect file permissions on ${FILE}"; fi
 1702             return 0
 1703         else
 1704             return 1
 1705         fi
 1706     }
 1707 
 1708 
 1709     ################################################################################
 1710     # Name        : IsVerbose()
 1711     # Description : Check if --verbose option is used to show more details on screen
 1712     #
 1713     # Parameters  : <none>
 1714     # Returns     : exit code (0 =true, 1 =false)
 1715     ################################################################################
 1716 
 1717     IsVerbose() {
 1718         if [ ${VERBOSE} -eq 1 ]; then return 0; else return 1; fi
 1719     }
 1720 
 1721 
 1722     ################################################################################
 1723     # Name        : IsVirtualMachine()
 1724     # Description : Determine whether it is a virtual machine
 1725     # Parameters  : <none>
 1726     # Returns     : exit code (0 = True, 1 = False, 2 = Unknown)
 1727     #               variable: ISVIRTUALMACHINE (0-2)
 1728     #               variable: VMTYPE
 1729     #               variable: VMFULLTYPE
 1730     ################################################################################
 1731 
 1732     IsVirtualMachine() {
 1733         LogText "Test: Determine if this system is a virtual machine"
 1734         # 0 = no, 1 = yes, 2 = unknown
 1735         ISVIRTUALMACHINE=2; VMTYPE="unknown"; VMFULLTYPE="Unknown"
 1736         SHORT=""
 1737 
 1738         if [ ${SKIP_VM_DETECTION} -eq 1 ]; then
 1739             return 2
 1740         fi
 1741 
 1742         # lxc environ detection
 1743         if [ -z "${SHORT}" ]; then
 1744             if [ -f /proc/1/environ ]; then
 1745                 FIND=$(grep -qa 'container=lxc' /proc/1/environ 2> /dev/null)
 1746                 if [ $? -eq 0 ]; then
 1747                     SHORT=lxc
 1748                     LogText "Result: found ${SHORT}"
 1749                 fi
 1750             fi
 1751         else
 1752             LogText "Result: skipped lxc environ detection test, as we already found machine type"
 1753         fi
 1754 
 1755         # facter
 1756         if [ -z "${SHORT}" ]; then
 1757             if [ -x /usr/bin/facter ] || [ -x /usr/local/bin/facter ]; then
 1758                 case "$(facter is_virtual)" in
 1759                 "true")
 1760                     SHORT=$(facter virtual)
 1761                     LogText "Result: found ${SHORT}"
 1762                 ;;
 1763                 "false")
 1764                     LogText "Result: facter says this machine is not a virtual"
 1765                 ;;
 1766                 esac
 1767             else
 1768                 LogText "Result: facter utility not found"
 1769             fi
 1770         else
 1771             LogText "Result: skipped facter test, as we already found machine type"
 1772         fi
 1773 
 1774         # systemd
 1775         if [ -z "${SHORT}" ]; then
 1776             if [ -x /usr/bin/systemd-detect-virt ]; then
 1777                 LogText "Test: trying to guess virtualization technology with systemd-detect-virt"
 1778                 FIND=$(/usr/bin/systemd-detect-virt)
 1779                 if [ -n "${FIND}" ]; then
 1780                     LogText "Result: found ${FIND}"
 1781                     SHORT="${FIND}"
 1782                 fi
 1783             else
 1784                 LogText "Result: systemd-detect-virt not found"
 1785             fi
 1786         else
 1787             LogText "Result: skipped systemd test, as we already found machine type"
 1788         fi
 1789 
 1790         # lscpu
 1791         # Values: VMware
 1792         if [ -z "${SHORT}" ]; then
 1793             if [ -x /usr/bin/lscpu ]; then
 1794                 LogText "Test: trying to guess virtualization with lscpu"
 1795                 FIND=$(lscpu | grep -i "^Hypervisor Vendor" | awk -F: '{ print $2 }' | sed 's/ //g')
 1796                 if [ -n "${FIND}" ]; then
 1797                     LogText "Result: found ${FIND}"
 1798                     SHORT="${FIND}"
 1799                 else
 1800                     LogText "Result: can't find hypervisor vendor with lscpu"
 1801                 fi
 1802             else
 1803                 LogText "Result: lscpu not found"
 1804             fi
 1805         else
 1806             LogText "Result: skipped lscpu test, as we already found machine type"
 1807         fi
 1808 
 1809         # dmidecode
 1810         # Values: VMware Virtual Platform / VirtualBox
 1811         if [ -z "${SHORT}" ]; then
 1812             # Try to find dmidecode in case we did not check binaries (e.g. lynis show environment)
 1813             if [ ${CHECK_BINARIES} -eq 0 ]; then DMIDECODEBINARY=$(command -v dmidecode 2> /dev/null); fi
 1814             if [ -n "${DMIDECODEBINARY}" -a -x "${DMIDECODEBINARY}" -a ${PRIVILEGED} -eq 1 ]; then
 1815                 LogText "Test: trying to guess virtualization with dmidecode"
 1816                 FIND=$(${DMIDECODEBINARY} -s system-product-name | awk '{ print $1 }')
 1817                 if [ -n "${FIND}" ]; then
 1818                     LogText "Result: found ${FIND}"
 1819                     SHORT="${FIND}"
 1820                 else
 1821                     LogText "Result: can't find product name with dmidecode"
 1822                 fi
 1823             else
 1824                 LogText "Result: dmidecode not found (or no access)"
 1825             fi
 1826         else
 1827             LogText "Result: skipped dmidecode test, as we already found machine type"
 1828         fi
 1829 
 1830         # Other options
 1831         # SaltStack: salt-call grains.get virtual
 1832         # < needs snippet >
 1833 
 1834         # Try common guest processes
 1835         if [ -z "${SHORT}" ]; then
 1836             LogText "Test: trying to guess virtual machine type by running processes"
 1837 
 1838             # VMware
 1839             if IsRunning vmware-guestd; then SHORT="vmware"
 1840             elif IsRunning vmtoolsd; then SHORT="vmware"
 1841             fi
 1842 
 1843             # VirtualBox based on guest services
 1844             if IsRunning vboxguest-service; then SHORT="virtualbox"
 1845             elif IsRunning VBoxClient; then SHORT="virtualbox"
 1846             elif IsRunning VBoxService; then SHORT="virtualbox"
 1847             fi
 1848         else
 1849             LogText "Result: skipped processes test, as we already found platform"
 1850         fi
 1851 
 1852         # Amazon EC2
 1853         if [ -z "${SHORT}" ]; then
 1854             LogText "Test: checking specific files for Amazon"
 1855             if [ -f /etc/ec2_version -a -s /etc/ec2_version ]; then
 1856                 SHORT="amazon-ec2"
 1857             else
 1858                 LogText "Result: system not hosted on Amazon"
 1859             fi
 1860         else
 1861             LogText "Result: skipped Amazon EC2 test, as we already found platform"
 1862         fi
 1863 
 1864         # sysctl values
 1865         if [ -z "${SHORT}" ]; then
 1866             LogText "Test: trying to guess virtual machine type by sysctl keys"
 1867 
 1868             # FreeBSD: hw.hv_vendor (remains empty for VirtualBox)
 1869             # NetBSD: machdep.dmi.system-product
 1870             # OpenBSD: hw.product
 1871             FIND=$(sysctl -a 2> /dev/null | grep -E "(hw.product|machdep.dmi.system-product)" | head -1 | sed 's/ = /=/' | awk -F= '{ print $2 }')
 1872             if [ ! "${FIND}" = "" ]; then
 1873                 SHORT="${FIND}"
 1874             fi
 1875         else
 1876             LogText "Result: skipped sysctl test, as we already found platform"
 1877         fi
 1878 
 1879         # lshw
 1880         if [ -z "${SHORT}" ]; then
 1881             if [ ${PRIVILEGED} -eq 1 ]; then
 1882                 if [ -x /usr/bin/lshw ]; then
 1883                     LogText "Test: trying to guess virtualization with lshw"
 1884                     FIND=$(lshw -quiet -class system 2> /dev/null | awk '{ if ($1=="product:") { print $2 }}')
 1885                     if HasData "${FIND}"; then
 1886                         LogText "Result: found ${FIND}"
 1887                         SHORT="${FIND}"
 1888                     fi
 1889                 else
 1890                     LogText "Result: lshw not found"
 1891                 fi
 1892             else
 1893                 LogText "Result: skipped lshw test, as we are non-privileged and need more permissions to run lshw"
 1894             fi
 1895         else
 1896             LogText "Result: skipped lshw test, as we already found machine type"
 1897         fi
 1898 
 1899         # Check if we caught some string along all tests
 1900         if [ -n "${SHORT}" ]; then
 1901             # Lowercase and see if we found a match
 1902             SHORT=$(echo ${SHORT} | awk '{ print $1 }' | tr '[:upper:]' '[:lower:]')
 1903 
 1904                 case ${SHORT} in
 1905                     amazon-ec2)         ISVIRTUALMACHINE=1; VMTYPE="amazon-ec2";      VMFULLTYPE="Amazon AWS EC2 Instance"                 ;;
 1906                     bochs)              ISVIRTUALMACHINE=1; VMTYPE="bochs";           VMFULLTYPE="Bochs CPU emulation"                     ;;
 1907                     docker)             ISVIRTUALMACHINE=1; VMTYPE="docker";          VMFULLTYPE="Docker container"                        ;;
 1908                     kvm)                ISVIRTUALMACHINE=1; VMTYPE="kvm";             VMFULLTYPE="KVM"                                     ;;
 1909                     lxc)                ISVIRTUALMACHINE=1; VMTYPE="lxc";             VMFULLTYPE="Linux Containers"                        ;;
 1910                     lxc-libvirt)        ISVIRTUALMACHINE=1; VMTYPE="lxc-libvirt";     VMFULLTYPE="libvirt LXC driver (Linux Containers)"   ;;
 1911                     microsoft)          ISVIRTUALMACHINE=1; VMTYPE="microsoft";       VMFULLTYPE="Microsoft Virtual PC"                    ;;
 1912                     openvz)             ISVIRTUALMACHINE=1; VMTYPE="openvz";          VMFULLTYPE="OpenVZ"                                  ;;
 1913                     oracle|virtualbox)  ISVIRTUALMACHINE=1; VMTYPE="virtualbox";      VMFULLTYPE="Oracle VM VirtualBox"                    ;;
 1914                     qemu)               ISVIRTUALMACHINE=1; VMTYPE="qemu";            VMFULLTYPE="QEMU"                                    ;;
 1915                     systemd-nspawn)     ISVIRTUALMACHINE=1; VMTYPE="systemd-nspawn";  VMFULLTYPE="Systemd Namespace container"             ;;
 1916                     uml)                ISVIRTUALMACHINE=1; VMTYPE="uml";             VMFULLTYPE="User-Mode Linux (UML)"                   ;;
 1917                     vmware)             ISVIRTUALMACHINE=1; VMTYPE="vmware";          VMFULLTYPE="VMware product"                          ;;
 1918                     xen)                ISVIRTUALMACHINE=1; VMTYPE="xen";             VMFULLTYPE="XEN"                                     ;;
 1919                     zvm)                ISVIRTUALMACHINE=1; VMTYPE="zvm";             VMFULLTYPE="IBM z/VM"                                ;;
 1920                     openstack)          ISVIRTUALMACHINE=1; VMTYPE="openstack";       VMFULLTYPE="Openstack Nova"                          ;;
 1921                     *)                  LogText "Result: Unknown virtualization type, so most likely system is physical"                   ;;
 1922                 esac
 1923         fi
 1924 
 1925         # Check final status
 1926         if [ ${ISVIRTUALMACHINE} -eq 1 ]; then
 1927             LogText "Result: found virtual machine (type: ${VMTYPE}, ${VMFULLTYPE})"
 1928             Report "vm=1"
 1929             Report "vmtype=${VMTYPE}"
 1930         elif [ ${ISVIRTUALMACHINE} -eq 2 ]; then
 1931             LogText "Result: unknown if this system is a virtual machine"
 1932             Report "vm=2"
 1933         else
 1934             LogText "Result: system seems to be non-virtual"
 1935         fi
 1936     }
 1937 
 1938 
 1939     ################################################################################
 1940     # Name        : IsWorldReadable()
 1941     # Description : Determines if a file is readable for all users (world)
 1942     #
 1943     # Input       : $1 = path (string)
 1944     # Returns     : exit code (0 = readable, 1 = not readable, 255 = error)
 1945     # Usage       : if IsWorldReadable /etc/motd; then echo "File is readable"; fi
 1946     ################################################################################
 1947 
 1948     IsWorldReadable() {
 1949         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling IsWorldReadable function"; fi
 1950         sFILE=$1
 1951         # Check for symlink
 1952         if [ -L ${sFILE} ]; then
 1953             ShowSymlinkPath ${sFILE}
 1954             if [ ! "${SYMLINK}" = "" ]; then sFILE="${SYMLINK}"; fi
 1955         fi
 1956         if [ -f ${sFILE} -o -d ${sFILE} ]; then
 1957             FINDVAL=$(ls -ld ${sFILE} | cut -c 8)
 1958             if [ "${FINDVAL}" = "r" ]; then return 0; else return 1; fi
 1959         else
 1960             return 255
 1961         fi
 1962     }
 1963 
 1964 
 1965     ################################################################################
 1966     # Name        : IsWorldExecutable()
 1967     # Description : Determines if a file is executable for all users (world)
 1968     #
 1969     # Input       : $1 = path (string)
 1970     # Returns     : exit code (0 = executable, 1 = not executable, 255 = error)
 1971     # Usage       : if IsWorldExecutable /bin/ps; then echo "File is executable"; fi
 1972     ################################################################################
 1973 
 1974     # Function IsWorldExecutable
 1975     IsWorldExecutable() {
 1976         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling IsWorldExecutable function"; fi
 1977         sFILE=$1
 1978         # Check for symlink
 1979         if [ -L ${sFILE} ]; then
 1980             ShowSymlinkPath ${sFILE}
 1981             if [ ! "${SYMLINK}" = "" ]; then sFILE="${SYMLINK}"; fi
 1982         fi
 1983         if [ -f ${sFILE} -o -d ${sFILE} ]; then
 1984             FINDVAL=$(ls -l ${sFILE} | cut -c 10)
 1985             if [ "${FINDVAL}" = "x" ]; then return 0; else return 1; fi
 1986         else
 1987             return 255
 1988         fi
 1989     }
 1990 
 1991 
 1992     ################################################################################
 1993     # Name        : IsWorldWritable()
 1994     # Description : Determines if a file is writable for all users
 1995     #
 1996     # Parameters  : $1 = path
 1997     # Returns     : exit code (0 = writable, 1 = not writable, 255 = error)
 1998     # Usage       : if IsWorldWritable /etc/motd; then echo "File is writable"; fi
 1999     ################################################################################
 2000 
 2001     IsWorldWritable() {
 2002         if [ $# -eq 0 ]; then ExitFatal "Missing parameter when calling IsWorldWritable function"; fi
 2003         sFILE=$1
 2004         FileIsWorldWritable=""
 2005 
 2006         # Only check if target is a file or directory
 2007         if [ -f ${sFILE} -o -d ${sFILE} ]; then
 2008             FINDVAL=$(ls -ld ${sFILE} | cut -c 9)
 2009             if IsDeveloperMode; then Debug "File mode of ${sFILE} is ${FINDVAL}"; fi
 2010             if [ "${FINDVAL}" = "w" ]; then return 0; else return 1; fi
 2011         else
 2012             return 255
 2013         fi
 2014     }
 2015 
 2016 
 2017     ################################################################################
 2018     # Name        : LogText()
 2019     # Description : Function logtext (redirect data ($1) to log file)
 2020     #
 2021     # Parameters  : $1 = text (string)
 2022     # Returns     : <nothing>
 2023     # Usage       : LogText "This line goes into the log file"
 2024     ################################################################################
 2025 
 2026     LogText() {
 2027         if [ ! "${LOGFILE}" = "" -a ${LOGTEXT} -eq 1 ]; then CDATE=$(date "+%Y-%m-%d %H:%M:%S"); echo "${CDATE} $1" >> ${LOGFILE}; fi
 2028     }
 2029 
 2030 
 2031     ################################################################################
 2032     # Name        : LogTextBreak()
 2033     # Description : Add a separator to log file between sections, tests etc
 2034     # Returns     : <nothing>
 2035     ################################################################################
 2036 
 2037     LogTextBreak() {
 2038         if [ ! "${LOGFILE}" = "" -a ${LOGTEXT} -eq 1 ]; then
 2039             CDATE=$(date "+%Y-%m-%d %H:%M:%S")
 2040             echo "${CDATE} ====" >> ${LOGFILE}
 2041         fi
 2042     }
 2043 
 2044 
 2045     ################################################################################
 2046     # Name        : PackageIsInstalled()
 2047     # Description : Determines if a package is installed
 2048     # Returns     : exit code
 2049     # Notes       : this function is not used yet, but created in advance to allow
 2050     #               the addition of support for all operating systems
 2051     ################################################################################
 2052 
 2053     PackageIsInstalled() {
 2054         exit_code=255
 2055 
 2056         # First parameter is package name (or __dummy__ for initial test to see if package manager is available and works as expected)
 2057         if [ $# -eq 1 ]; then
 2058             package="$1"
 2059         else
 2060             Fatal "Incorrect usage of PackageIsInstalled function"
 2061         fi
 2062 
 2063         if [ -n "${DNFBINARY}" ]; then
 2064             output=$(${DNFBINARY} --quiet --cacheonly --noplugins --assumeno info --installed ${package} > /dev/null 2>&1)
 2065             exit_code=$?
 2066         elif [ -n "${DPKGBINARY}" ]; then
 2067             output=$(${DPKGBINARY} -l ${package} 2> /dev/null | ${GREPBINARY} "^ii")
 2068             exit_code=$?
 2069         elif [ -n "${EQUERYBINARY}" ]; then
 2070             output=$(${EQUERYBINARY} --quiet ${package} > /dev/null 2>&1)
 2071             exit_code=$?  # 0=package installed, 3=package not installed
 2072         elif [ -n "${PACMANBINARY}" ]; then
 2073             output=$(${PACMANBINARY} -Qs ${PKG} | grep "local/${PKG} " >/dev/null 2>&1)
 2074             exit_code=$?  # 0=package installed, 1=package not installed
 2075         elif [ -n "${PKG_BINARY}" ]; then
 2076             output=$(${PKG_BINARY} -N info ${package} >/dev/null 2>&1)
 2077             exit_code=$?  # 0=package installed, 70=invalid package
 2078         elif [ -n "${PKGINFOBINARY}" ]; then
 2079             output=$(${PKGINFOBINARY} -q -e ${package} >/dev/null 2>&1)
 2080             exit_code=$?  # 0=package installed, 1=package not installed
 2081         elif [ -n "${RPMBINARY}" ]; then
 2082             output=$(${RPMBINARY} --quiet -q ${package} > /dev/null 2>&1)
 2083             exit_code=$?
 2084         elif [ -n "${SWUPDBINARY}" ]; then
 2085             output=$(${SWUPDBINARY} bundle-list > /dev/null 2>&1 | ${GREPBINARY} "^${package}$")
 2086             exit_code=$?
 2087         elif [ -n "${ZYPPERBINARY}" ]; then
 2088             output=$(${ZYPPERBINARY} --quiet --non-interactive search --installed -i ${package} 2> /dev/null | grep "^i")
 2089             if [ -n "${output}" ]; then exit_code=0; else exit_code=1; fi
 2090         elif [ -n "${XBPSBINARY}" ]; then
 2091             output=$(${XBPSBINARY} ${package} 2> /dev/null | ${GREPBINARY} "^ii")
 2092             exit_code=$?
 2093         else
 2094             if [ "${package}" != "__dummy__" ]; then
 2095                 ReportException "PackageIsInstalled:01 (test=${TEST_NO:-unknown})"
 2096             fi
 2097         fi
 2098 
 2099         # Give thumbs up if dummy package is used during initial test for package manager availability
 2100         if [ "${package}" = "__dummy__" ]; then
 2101             # There should be no positive match on this dummy package
 2102             if [ ${exit_code} -eq 0 ]; then
 2103                 exit_code=1
 2104             elif [ ${exit_code} -eq 255 ]; then
 2105                 exit_code=1
 2106             else
 2107                 exit_code=0
 2108             fi
 2109         fi
 2110 
 2111         return ${exit_code}
 2112     }
 2113 
 2114 
 2115     ################################################################################
 2116     # Name        : ParseProfiles()
 2117     # Description : Check file permissions and parse data from profiles
 2118     # Parameters  : <none>
 2119     # Returns     : <nothing>
 2120     ################################################################################
 2121 
 2122     ParseProfiles() {
 2123         SafePerms ${INCLUDEDIR}/profiles
 2124         . ${INCLUDEDIR}/profiles
 2125     }
 2126 
 2127 
 2128     ################################################################################
 2129     # Name        : ParseTestValues()
 2130     # Description : Parse values from a specific test
 2131     #
 2132     # Parameters  : $1 = service (e.g. ssh)
 2133     # Returns     : CHECK_VALUES_ARRAY variable
 2134     ################################################################################
 2135 
 2136     ParseTestValues() {
 2137         RETVAL=1
 2138         FOUND=0
 2139         CHECK_VALUES_ARRAY=""
 2140         if [ $# -gt 0 -a ! "${CHECK_OPTION_ARRAY}" = "" ]; then
 2141             for I in ${CHECK_OPTION_ARRAY}; do
 2142                 Debug "Array value: ${I}"
 2143                 SPLIT=$(echo ${I} | sed 's/,/\n/g')
 2144                 for ITEM in ${SPLIT}; do
 2145                     FUNCTION=""
 2146                     VALUE=""
 2147                     SEARCH=""
 2148                     SERVICE=""
 2149                     ITEM_KEY=$(echo ${ITEM} | awk -F: '{print $1}')
 2150                     ITEM_VALUE=$(echo ${ITEM} | awk -F: '{print $2}')
 2151                     Debug "Array item: ${ITEM_KEY} with value ${ITEM_VALUE}"
 2152                     case ${ITEM_KEY} in
 2153                         "function")
 2154                             case ${ITEM_VALUE} in
 2155                                 "equals")
 2156                                     FUNCTION="eq"
 2157                                 ;;
 2158                             esac
 2159                         ;;
 2160                         "service")
 2161                             if [ "${ITEM_VALUE}" = "$1" ]; then
 2162                                 FOUND=1
 2163                             fi
 2164                         ;;
 2165                         "value")
 2166                             VALUE="${ITEM_VALUE}"
 2167                         ;;
 2168                         *)
 2169                             echo "Incorrect call to function ParseTestValues"; ExitFatal
 2170                         ;;
 2171                     esac
 2172                     if [ ${FOUND} -eq 1 ]; then
 2173                         CHECK_VALUES_ARRAY="${CHECK_VALUES_ARRAY} ${SEARCH}:${VALUE}:${FUNCTION}:"
 2174                         FOUND=0
 2175                     fi
 2176                 done
 2177             done
 2178             RETVAL=1
 2179         fi
 2180         return ${RETVAL}
 2181     }
 2182 
 2183 
 2184     ################################################################################
 2185     # Name        : ParseNginx()
 2186     # Description : Parse nginx configuration lines
 2187     #
 2188     # Parameters  : $1 = file (should be readable and tested upfront)
 2189     # Returns     : <nothing>
 2190     ################################################################################
 2191 
 2192     ParseNginx() {
 2193         COUNT=0
 2194         BREADCRUMB=""
 2195         if [ $# -eq 0 ]; then ExitFatal "No arguments provided to ParseNginx()"; fi
 2196         CONFIG_FILE=$1
 2197 
 2198         # Create temporary files
 2199         CreateTempFile || ExitFatal "Could not create temporary file"
 2200         TMP_NGINX_FILE_RAW="${TEMP_FILE}"
 2201         CreateTempFile || ExitFatal "Could not create temporary file"
 2202         TMP_NGINX_FILE="${TEMP_FILE}"
 2203 
 2204         # Strip out spaces, tabs and line breaks
 2205         awk '{$1=$1;print $0}' ${CONFIG_FILE} > ${TMP_NGINX_FILE_RAW}
 2206         # Now clean up the file further (combine lines, remove commented lines and empty lines)
 2207         sed 's#\\$##g' ${TMP_NGINX_FILE_RAW} | grep -v "^#" | grep -v "^$" > ${TMP_NGINX_FILE}
 2208 
 2209         LogText "Action: parsing configuration file ${CONFIG_FILE}"
 2210         COUNT=$(( COUNT + 1))
 2211         FIND=$(sed 's/ /:space:/g' ${TMP_NGINX_FILE})
 2212         DEPTH=0
 2213         for I in ${FIND}; do
 2214             I=$(echo ${I} | sed 's/:space:/ /g' | sed 's/;$//' | sed 's/ #.*$//')
 2215             OPTION=$(echo ${I} | awk '{ print $1 }')
 2216             # Use quotes here to prevent wildcard expansion
 2217             VALUE=$(echo "${I}"| cut -d' ' -f2-)
 2218             LogText "Result: found option ${OPTION} in ${CONFIG_FILE} with value '${VALUE}'"
 2219             STORE_SETTING=1
 2220             case ${OPTION} in
 2221                 "events")
 2222                     BREADCRUMB="${BREADCRUMB}/events"
 2223                     DEPTH=$(( DEPTH + 1))
 2224                     STORE_SETTING=0
 2225                     NGINX_EVENTS_COUNTER=$(( NGINX_EVENTS_COUNTER + 1 ))
 2226                 ;;
 2227                 "http")
 2228                     BREADCRUMB="${BREADCRUMB}/http"
 2229                     DEPTH=$(( DEPTH + 1))
 2230                     STORE_SETTING=0
 2231                     NGINX_HTTP_COUNTER=$(( NGINX_HTTP_COUNTER + 1 ))
 2232                 ;;
 2233                 "location")
 2234                     BREADCRUMB="${BREADCRUMB}/location"
 2235                     DEPTH=$(( DEPTH + 1))
 2236                     STORE_SETTING=0
 2237                     NGINX_LOCATION_COUNTER=$(( NGINX_LOCATION_COUNTER + 1 ))
 2238                 ;;
 2239                 "server")
 2240                     BREADCRUMB="${BREADCRUMB}/server"
 2241                     DEPTH=$(( DEPTH + 1))
 2242                     STORE_SETTING=0
 2243                     NGINX_SERVER_COUNTER=$(( NGINX_SERVER_COUNTER + 1 ))
 2244                 ;;
 2245                 "}")
 2246                     BREADCRUMB=$(echo ${BREADCRUMB} | awk -F/ 'sub(FS $NF,x)')
 2247                     DEPTH=$(( DEPTH - 1))
 2248                     STORE_SETTING=0
 2249                 ;;
 2250                 access_log)
 2251                     if [ "${VALUE}" = "off" ]; then
 2252                         LogText "Result: found logging disabled for one virtual host"
 2253                         NGINX_ACCESS_LOG_DISABLED=1
 2254                     else
 2255                         if [ ! "${VALUE}" = "" ]; then
 2256                             # If multiple values follow, select first one
 2257                             VALUE=$(echo ${VALUE} | awk '{ print $1 }')
 2258                             # Find both, log files provided with full or relative path
 2259                             if [ ! -f ${VALUE} -a ! -f "${CONFIG_FILE%nginx.conf}${VALUE}" ]; then
 2260                                 LogText "Result: could not find log file ${VALUE} referenced in nginx configuration"
 2261                                 NGINX_ACCESS_LOG_MISSING=1
 2262                             fi
 2263                         fi
 2264                     fi
 2265                 ;;
 2266                 # Headers
 2267                 add_header)
 2268                     HEADER=$(echo ${VALUE} | awk '{ print $1 }')
 2269                     HEADER_VALUE=$(echo ${VALUE} | cut -d' ' -f2-)
 2270                     LogText "Result: found header ${HEADER} with value ${HEADER_VALUE}"
 2271                     #Report "nginx_header[]=${HEADER}|${HEADER_VALUE}|"
 2272                 ;;
 2273                 alias)
 2274                     NGINX_ALIAS_FOUND=1
 2275                 ;;
 2276                 allow)
 2277                     NGINX_ALLOW_FOUND=1
 2278                 ;;
 2279                 autoindex)
 2280                 ;;
 2281                 deny)
 2282                     NGINX_DENY_FOUND=1
 2283                 ;;
 2284                 expires)
 2285                     NGINX_EXPIRES_FOUND=1
 2286                 ;;
 2287                 error_log)
 2288                     # Check if debug is appended
 2289                     FIND=$(echo ${VALUE} | awk '{ if ($2=="debug") { print 1 } else { print 0 }}')
 2290                     if [ ${FIND} -eq 1 ]; then
 2291                         NGINX_ERROR_LOG_DEBUG=1
 2292                     fi
 2293                     # Check if log file exists
 2294                     FILE=$(echo ${VALUE} | awk '{ print $1 }')
 2295                     if [ ! "${FILE}" = "" ]; then
 2296                         # Find both, log files provided with full or relative path
 2297                         if [ ! -f ${FILE} -a ! -f "${CONFIG_FILE%nginx.conf}${FILE}" ]; then
 2298                             NGINX_ERROR_LOG_MISSING=1
 2299                         fi
 2300                     else
 2301                         LogText "Warning: did not find a filename after error_log in nginx configuration"
 2302                     fi
 2303                 ;;
 2304                 error_page)
 2305                 ;;
 2306                 fastcgi_intercept_errors)
 2307                 ;;
 2308                 fastcgi_param)
 2309                     NGINX_FASTCGI_FOUND=1
 2310                     NGINX_FASTCGI_PARAMS_FOUND=1
 2311                 ;;
 2312                 fastcgi_pass)
 2313                     NGINX_FASTCGI_FOUND=1
 2314                     NGINX_FASTCGI_PASS_FOUND=1
 2315                 ;;
 2316                 fastcgi_pass_header)
 2317                 ;;
 2318                 include)
 2319                     if [ -f "${VALUE}" ]; then
 2320                         FOUND=0
 2321                         for CONF in ${NGINX_CONF_FILES}; do
 2322                             if [ "${CONF}" = "${VALUE}" ]; then FOUND=1; LogText "Found this file already in our configuration files array, not adding to queue"; fi
 2323                         done
 2324                         for CONF in ${NGINX_CONF_FILES_ADDITIONS}; do
 2325                             if [ "${CONF}" = "${VALUE}" ]; then FOUND=1; LogText "Found this file already in our configuration files array (additions), not adding to queue"; fi
 2326                         done
 2327                         if [ ${FOUND} -eq 0 ]; then NGINX_CONF_FILES_ADDITIONS="${NGINX_CONF_FILES_ADDITIONS} ${VALUE}"; fi
 2328                     # Check if include value is a relative path only
 2329                     elif [ -f "${CONFIG_FILE%nginx.conf}${VALUE%;*}" ]; then
 2330                         VALUE="${CONFIG_FILE%nginx.conf}${VALUE}"
 2331                         FOUND=0
 2332                         for CONF in ${NGINX_CONF_FILES}; do
 2333                             if [ "${CONF}" = "${VALUE}" ]; then FOUND=1; LogText "Found this file already in our configuration files array, not adding to queue"; fi
 2334                         done
 2335                         for CONF in ${NGINX_CONF_FILES_ADDITIONS}; do
 2336                             if [ "${CONF}" = "${VALUE}" ]; then FOUND=1; LogText "Found this file already in our configuration files array (additions), not adding to queue"; fi
 2337                         done
 2338                         if [ ${FOUND} -eq 0 ]; then NGINX_CONF_FILES_ADDITIONS="${NGINX_CONF_FILES_ADDITIONS} ${VALUE}"; fi
 2339                     # Check for additional config files included as follows
 2340                     # "include sites-enabled/*.conf" (relative path)
 2341                     # "include /etc/nginx/sites-enabled/*.conf" (absolute path)
 2342                     elif [ $(echo "${VALUE}" | grep -F -c "*.conf") -gt 0 ]; then
 2343                         # Check if path is absolute or relative
 2344                         case $VALUE in
 2345                             /*)
 2346                                 # Absolute path, so wildcard pattern is already correct
 2347                                 CONF_WILDCARD=${VALUE%;*}
 2348                             ;;
 2349                             *)
 2350                                 # Relative path, so construct absolute path for wildcard pattern
 2351                                 CONF_WILDCARD=${CONFIG_FILE%nginx.conf}${VALUE%;*}
 2352                             ;;
 2353                         esac
 2354                         for FOUND_CONF in ${CONF_WILDCARD}; do
 2355                             if [ "${FOUND_CONF}" = "${CONF_WILDCARD}" ]; then
 2356                                 LogText "Found no match for wildcard pattern: ${CONF_WILDCARD}"
 2357                                 break
 2358                             fi
 2359                             FOUND=0
 2360                             for CONF in ${NGINX_CONF_FILES}; do
 2361                                 if [ "${CONF}" = "${FOUND_CONF}" ]; then FOUND=1; LogText "Found this file already in our configuration files array, not adding to queue"; fi
 2362                             done
 2363                             for CONF in ${NGINX_CONF_FILES_ADDITIONS}; do
 2364                                 if [ "${CONF}" = "${FOUND_CONF}" ]; then FOUND=1; LogText "Found this file already in our configuration files array (additions), not adding to queue"; fi
 2365                             done
 2366                             if [ ${FOUND} -eq 0 ]; then NGINX_CONF_FILES_ADDITIONS="${NGINX_CONF_FILES_ADDITIONS} ${FOUND_CONF}"; fi
 2367                         done
 2368                     else
 2369                         LogText "Result: this include does not point to a file"
 2370                     fi
 2371                 ;;
 2372                 index)
 2373                 ;;
 2374                 keepalive_timeout)
 2375                 ;;
 2376                 listen)
 2377                     NGINX_LISTEN_FOUND=1
 2378                     # Test for ssl on listen statement
 2379                     FIND_SSL=$(echo ${VALUE} | grep ssl)
 2380                     if [ ! "${FIND_SSL}" = "" ]; then NGINX_SSL_ON=1; fi
 2381                 ;;
 2382                 location)
 2383                     NGINX_LOCATION_FOUND=1
 2384                 ;;
 2385                 return)
 2386                     NGINX_RETURN_FOUND=1
 2387                 ;;
 2388                 root)
 2389                     NGINX_ROOT_FOUND=1
 2390                 ;;
 2391                 server_name)
 2392                 ;;
 2393                 ssl)
 2394                     if [ "${VALUE}" = "on" ]; then NGINX_SSL_ON=1; fi
 2395                 ;;
 2396                 ssl_certificate)
 2397                     LogText "Found SSL certificate in nginx configuration"
 2398                 ;;
 2399                 ssl_certificate_key)
 2400                 ;;
 2401                 ssl_ciphers)
 2402                     NGINX_SSL_CIPHERS=1
 2403                 ;;
 2404                 ssl_prefer_server_ciphers)
 2405                     if [ "${VALUE}" = "on" ]; then NGINX_SSL_PREFER_SERVER_CIPHERS=1; fi
 2406                 ;;
 2407                 ssl_protocols)
 2408                     NGINX_SSL_PROTOCOLS=1
 2409                     VALUE=$(echo ${VALUE} | sed 's/;$//' | tr '[:upper:]' '[:lower:]')
 2410                     for ITEM in ${VALUE}; do
 2411                         LogText "Result: found protocol ${ITEM}"
 2412                         case ${ITEM} in
 2413                             "sslv2" | "sslv3" | "tlsv1")
 2414                                 NGINX_WEAK_SSL_PROTOCOL_FOUND=1
 2415                             ;;
 2416                         esac
 2417                         Report "ssl_tls_protocol_enabled[]=${ITEM}"
 2418                         ReportDetails --service nginx --field protocol --value "${ITEM}"
 2419                     done
 2420                 ;;
 2421                 ssl_session_cache)
 2422                 ;;
 2423                 ssl_session_timeout)
 2424                 ;;
 2425                 types)
 2426                 ;;
 2427                 *)
 2428                     LogText "Found unknown option ${OPTION} in nginx configuration"
 2429                 ;;
 2430             esac
 2431             if [ ${STORE_SETTING} -eq 1 ]; then
 2432                 CONFIG_TREE="${BREADCRUMB}"
 2433                 if [ -z "${CONFIG_TREE}" ]; then CONFIG_TREE="/"; fi
 2434                 if [ -z "${OPTION}" ]; then OPTION="NA"; fi
 2435                 if [ -z "${VALUE}" ]; then VALUE="NA"; fi
 2436                 StoreNginxSettings --config ${CONFIG_FILE} --tree ${CONFIG_TREE} --depth ${DEPTH} --setting ${OPTION} --value "${VALUE}"
 2437             fi
 2438         done
 2439     }
 2440 
 2441 
 2442     ################################################################################
 2443     # Name        : PortIsListening()
 2444     # Description : Check if machine is listening on specified protocol and port
 2445     #
 2446     # Parameters  : $1 = protocol
 2447     #               $2 = port
 2448     # Returns     : exit code (0 = listening, 1 = not listening, 255 = can't perform test)
 2449     # Usage       : if PortIsListening "TCP" 22; then echo "Port is listening"; fi
 2450     ################################################################################
 2451 
 2452     PortIsListening() {
 2453         if [ -z "${LSOFBINARY}" ]; then
 2454             return 255
 2455         else
 2456             if [ $# -eq 2 ] && [ $1 = "TCP" -o $1 = "UDP" ]; then
 2457                 LogText "Test: find service listening on $1:$2"
 2458                 if [ $1 = "TCP" ]; then FIND=$(${LSOFBINARY}${LSOF_EXTRA_OPTIONS} -i${1} -s${1}:LISTEN -P -n | grep ":${2} "); else FIND=$(${LSOFBINARY}${LSOF_EXTRA_OPTIONS} -i${1} -P -n | grep ":${2} "); fi
 2459                 if [ ! "${FIND}" = "" ]; then
 2460                     LogText "Result: found service listening on port $2 ($1)"
 2461                     return 0
 2462                 else
 2463                     LogText "Result: did not find service listening on port $2 ($1)"
 2464                     return 1
 2465                 fi
 2466             else
 2467                 return 255
 2468                 ReportException ${TEST_NO} "Error in function call to PortIsListening"
 2469             fi
 2470         fi
 2471     }
 2472 
 2473 
 2474     ################################################################################
 2475     # Name        : Progress()
 2476     # Description : Displays progress on screen with dots
 2477     #
 2478     # Parameters  : $1 = --finish or text (string)
 2479     # Returns     : <nothing>
 2480     # Tip         : Use this function from Register with the --progress parameter
 2481     ################################################################################
 2482 
 2483     Progress() {
 2484         if [ ${CRONJOB} -eq 0 ]; then
 2485             if [ ${QUIET} -eq 0 ]; then
 2486                 if [ "$1" = "--finish" ]; then
 2487                     ${ECHOCMD} ""
 2488                 else
 2489                     # If the No-Break version of echo is known, use that (usually breaks in combination with -e)
 2490                     if [ ! "${ECHONB}" = "" ]; then
 2491                         ${ECHONB} "$1"
 2492                     else
 2493                         ${ECHOCMD} -en "$1"
 2494                     fi
 2495                 fi
 2496             fi
 2497         fi
 2498     }
 2499 
 2500 
 2501     ################################################################################
 2502     # Name        : RandomString()
 2503     # Description : Displays progress on screen with dots
 2504     #
 2505     # Parameters  : $1 = number (amount of characters, optional)
 2506     # Returns     : RANDOMSTRING variable
 2507     # Usage       : RandomString 32
 2508     ################################################################################
 2509 
 2510     RandomString() {
 2511         # Check a (pseudo) random character device
 2512         if [ -c /dev/urandom ]; then FILE="/dev/urandom"
 2513         elif [ -c /dev/random ]; then FILE="/dev/random"
 2514         else
 2515           Display "Can not use RandomString function, as there is no random device to be used"
 2516         fi
 2517         if [ $# -eq 0 ]; then SIZE=16; else SIZE=$1; fi
 2518         CSIZE=$((SIZE / 2))
 2519         RANDOMSTRING=$(head -c ${CSIZE} /dev/urandom | od -An -x | tr -d ' ' | cut -c 1-${SIZE})
 2520     }
 2521 
 2522 
 2523     ################################################################################
 2524     # Name        : Readonly()
 2525     # Description : Mark a variable as read-only data
 2526     #
 2527     # Returns     : <nothing>
 2528     # Notes       : new function, not in use yet
 2529     ################################################################################
 2530 
 2531     Readonly() {
 2532         if [ $# -eq 1 ]; then
 2533             if type -t typeset; then
 2534                 typeset -r $1
 2535             else
 2536                 Debug "No typeset available to mark variable '$1' as read-only variable"
 2537             fi
 2538         else
 2539             ExitFatal "Expected 1 parameter, received none or multiple"
 2540         fi
 2541     }
 2542 
 2543 
 2544     ################################################################################
 2545     # Name        : Register()
 2546     # Description : Register a test and see if it has to be run
 2547     #
 2548     # Parameters  : multiple, see test
 2549     # Returns     : SKIPTEST (0 or 1)
 2550     ################################################################################
 2551 
 2552     GetTimestamp() {
 2553         ts=0
 2554         case "${OS}" in
 2555             "Linux")
 2556                 ts=$(date "+%s%N")
 2557             ;;
 2558             *)
 2559                 ts=$(date "+%s")
 2560             ;;
 2561         esac
 2562         echo $ts
 2563     }
 2564 
 2565     Register() {
 2566         # Do not insert a log break, if previous test was not logged
 2567         if [ ${SKIPLOGTEST} -eq 0 ]; then LogTextBreak; fi
 2568         ROOT_ONLY=0; SKIPTEST=0; SKIPLOGTEST=0; SKIPREASON=""; PREQS_MET=""
 2569         TEST_CATEGORY=""; TEST_NEED_NETWORK=""; TEST_NEED_OS=""; TEST_NEED_PKG_MGR=0; TEST_NEED_PLATFORM=""
 2570         TOTAL_TESTS=$((TOTAL_TESTS + 1))
 2571         while [ $# -ge 1 ]; do
 2572             case $1 in
 2573                 --category)
 2574                     shift
 2575                     TEST_CATEGORY=$1
 2576                 ;;
 2577                 --description)
 2578                     shift
 2579                     TEST_DESCRIPTION=$1
 2580                 ;;
 2581                 --platform)
 2582                     shift
 2583                     TEST_NEED_PLATFORM=$1
 2584                 ;;
 2585                 --network)
 2586                     shift
 2587                     TEST_NEED_NETWORK=$1
 2588                 ;;
 2589                 --os)
 2590                     shift
 2591                     TEST_NEED_OS=$1
 2592                 ;;
 2593                 --package-manager-required)
 2594                     TEST_NEED_PKG_MGR=1
 2595                 ;;
 2596                 --preqs-met)
 2597                     shift
 2598                     PREQS_MET=$1
 2599                 ;;
 2600                 --progress)
 2601                     Progress "."
 2602                 ;;
 2603                 --root-only)
 2604                     shift
 2605                     if [ "$1" = "YES" -o "$1" = "yes" ]; then
 2606                         ROOT_ONLY=1
 2607                     elif [ "$1" = "NO" -o "$1" = "no" ]; then
 2608                         ROOT_ONLY=0
 2609                     else
 2610                         Debug "Invalid option for --root-only parameter of Register function"
 2611                     fi
 2612                 ;;
 2613                 --skip-reason)
 2614                     shift
 2615                     SKIPREASON="$1"
 2616                 ;;
 2617                 --test-no)
 2618                     shift
 2619                     TEST_NO=$1
 2620                 ;;
 2621                 --weight)
 2622                     shift
 2623                     TEST_WEIGHT=$1
 2624                 ;;
 2625 
 2626                 *)
 2627                     echo "INVALID OPTION (Register): $1"
 2628                     exit 1
 2629                 ;;
 2630             esac
 2631             # Go to next parameter
 2632             shift
 2633         done
 2634 
 2635         # Measure timing
 2636         CURRENT_TS=$(GetTimestamp)
 2637         if [ ${PREVIOUS_TS} -gt 0 ]; then
 2638             SLOW_TEST=0
 2639             TIME_THRESHOLD=$SLOW_TEST_THRESHOLD  # seconds
 2640 
 2641             # Calculate timing and determine if we use seconds or nanoseconds (more precise)
 2642             TIME_DIFF=$((CURRENT_TS - PREVIOUS_TS))
 2643             if [ ${CURRENT_TS} -gt 1000000000000000000 ]; then
 2644                 TIME_DIFF_FORMAT="nanoseconds"
 2645                 TIME_THRESHOLD=$((TIME_THRESHOLD * 1000000000))
 2646                 if [ ${TIME_DIFF} -gt ${TIME_THRESHOLD} ]; then
 2647                     SLOW_TEST=1
 2648                     # Convert back to seconds for readability
 2649                     TIME_DIFF_FORMAT="seconds"
 2650                     TIME_DIFF=$(echo ${TIME_DIFF} | ${AWKBINARY} '{printf "%f",$1/1000000000}')
 2651                 fi
 2652             else
 2653                 TIME_DIFF_FORMAT="seconds"
 2654                 if [ ${TIME_DIFF} -gt ${TIME_THRESHOLD} ]; then
 2655                     SLOW_TEST=1
 2656                 fi
 2657             fi
 2658             if [ ${SLOW_TEST} -eq 1 ]; then
 2659                 DisplayWarning "Test ${PREVIOUS_TEST} had a long execution: ${TIME_DIFF} ${TIME_DIFF_FORMAT}"
 2660                 Report "slow_test[]=${PREVIOUS_TEST},${TIME_DIFF}"
 2661             fi
 2662         fi
 2663 
 2664         # Skip test if it's configured in profile (old style)
 2665         if [ ${SKIPTEST} -eq 0 ]; then
 2666             FIND=$(echo "${TEST_SKIP_ALWAYS}" | grep "${TEST_NO}" | tr '[:lower:]' '[:upper:]')
 2667             if [ ! "${FIND}" = "" ]; then SKIPTEST=1; SKIPREASON="Skipped by configuration"; fi
 2668         fi
 2669 
 2670         # Check if this test is on the list to skip
 2671         if [ ${SKIPTEST} -eq 0 ]; then
 2672             VALUE=$(echo ${TEST_NO} | tr '[:lower:]' '[:upper:]')
 2673             for I in ${SKIP_TESTS}; do
 2674                 if [ "${I}" = "${VALUE}" ]; then SKIPTEST=1; SKIPREASON="Skipped by profile setting (skip-test)"; fi
 2675             done
 2676         fi
 2677 
 2678         # Skip if test is not in the list
 2679         if [ ${SKIPTEST} -eq 0 -a ! "${TESTS_TO_PERFORM}" = "" ]; then
 2680             FIND=$(echo "${TESTS_TO_PERFORM}" | grep "${TEST_NO}")
 2681             if [ "${FIND}" = "" ]; then SKIPTEST=1; SKIPREASON="Test not in list of tests to perform"; fi
 2682         fi
 2683 
 2684         # Do not run scans which have a higher intensity than what we prefer
 2685         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
 2686         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
 2687 
 2688         # Test if our OS is the same as the requested OS (can be multiple values)
 2689         if [ ${SKIPTEST} -eq 0 -a -n "${TEST_NEED_OS}" ]; then
 2690             HASMATCH=0
 2691             for I in ${TEST_NEED_OS}; do
 2692                 if [ "${I}" = "${OS}" ]; then HASMATCH=1; fi
 2693             done
 2694             if [ ${HASMATCH} -eq 0 ]; then
 2695                 SKIPTEST=1; SKIPREASON="Incorrect guest OS (${TEST_NEED_OS} only)"
 2696                 if [ ${LOG_INCORRECT_OS} -eq 0 ]; then SKIPLOGTEST=1; fi
 2697             fi
 2698         fi
 2699 
 2700         # Skip test when it belongs to another category (default is 'all')
 2701         if [ ${SKIPTEST} -eq 0 -a -n "${TEST_CATEGORY_TO_CHECK}" -a ! "${TEST_CATEGORY_TO_CHECK}" = "all" -a ! "${TEST_CATEGORY}" = "${TEST_CATEGORY_TO_CHECK}" ]; then
 2702             SKIPTEST=1; SKIPREASON="Incorrect category (${TEST_CATEGORY_TO_CHECK} only)"
 2703         fi
 2704 
 2705         # Check for correct hardware platform
 2706         if [ ${SKIPTEST} -eq 0 -a -n "${TEST_NEED_PLATFORM}" ]; then
 2707             HASMATCH=0
 2708             for I in ${TEST_NEED_PLATFORM}; do
 2709                 if [ "${I}" = "${HARDWARE}" ]; then HASMATCH=1; fi
 2710             done
 2711             if [ ${HASMATCH} -eq 0 ]; then
 2712                 SKIPTEST=1; SKIPREASON="Incorrect hardware platform (${TEST_NEED_PLATFORM} only)"
 2713             fi
 2714         fi
 2715 
 2716         # Check for required (and discovered) package manager
 2717         if [ ${SKIPTEST} -eq 0 -a ${TEST_NEED_PKG_MGR} -eq 1 -a ${HAS_PACKAGE_MANAGER} -eq 0 ]; then SKIPTEST=1; SKIPREASON="Requires a known package manager to test presence of a particular package"; fi
 2718 
 2719         # Not all prerequisites met, like missing tool
 2720         if [ ${SKIPTEST} -eq 0 -a "${PREQS_MET}" = "NO" ]; then SKIPTEST=1; if [ -z "${SKIPREASON}" ]; then SKIPREASON="Prerequisites not met (ie missing tool, other type of Linux distribution)"; fi; fi
 2721 
 2722         # Skip if a test is root only and we are running a non-privileged test
 2723         if [ ${SKIPTEST} -eq 0 -a ${ROOT_ONLY} -eq 1 -a ! ${MYID} = "0" ]; then
 2724             SKIPTEST=1; SKIPREASON="This test needs root permissions"
 2725             SKIPPED_TESTS_ROOTONLY="${SKIPPED_TESTS_ROOTONLY}====${TEST_NO}:space:-:space:${TEST_DESCRIPTION}"
 2726             #SkipTest "${TEST_NO}:Test:space:requires:space:root:space:permissions:-:-:"
 2727         fi
 2728 
 2729         # Skip test?
 2730         if [ ${SKIPTEST} -eq 0 ]; then
 2731             # First wait X seconds (depending pause_between_tests)
 2732             if [ ${TEST_PAUSE_TIME} -gt 0 ]; then sleep ${TEST_PAUSE_TIME}; fi
 2733 
 2734             # Increase counter for every registered test which is performed
 2735             CountTests
 2736             if [ ${SKIPLOGTEST} -eq 0 ]; then
 2737                 LogText "Performing test ID ${TEST_NO} (${TEST_DESCRIPTION})"
 2738                 if IsVerbose; then Debug "Performing test ID ${TEST_NO} (${TEST_DESCRIPTION})"; fi
 2739             fi
 2740             TESTS_EXECUTED="${TEST_NO}|${TESTS_EXECUTED}"
 2741         else
 2742             if [ ${SKIPLOGTEST} -eq 0 ]; then LogText "Skipped test ${TEST_NO} (${TEST_DESCRIPTION})"; fi
 2743             if [ ${SKIPLOGTEST} -eq 0 ]; then LogText "Reason to skip: ${SKIPREASON}"; fi
 2744             TESTS_SKIPPED="${TEST_NO}|${TESTS_SKIPPED}"
 2745         fi
 2746         unset SKIPREASON
 2747 
 2748         # Save timestamp for next time the Register function is called
 2749         PREVIOUS_TEST="${TEST_NO}"
 2750         PREVIOUS_TS="${CURRENT_TS}"
 2751     }
 2752 
 2753 
 2754     ################################################################################
 2755     # Name        : RemoveColors()
 2756     # Description : Disabled colors by overriding them (defined in include/consts)
 2757     #
 2758     # Notes       : Can be activated using --no-colors
 2759     ################################################################################
 2760 
 2761     RemoveColors() {
 2762         COLORS=0  # disable most color selections
 2763 
 2764         # Normal color names
 2765         CYAN=""
 2766         BLUE=""
 2767         BROWN=""
 2768         DARKGRAY=""
 2769         GRAY=""
 2770         GREEN=""
 2771         LIGHTBLUE=""
 2772         MAGENTA=""
 2773         PURPLE=""
 2774         RED=""
 2775         YELLOW=""
 2776         WHITE=""
 2777 
 2778         # Colors with background
 2779         BG_BLUE=""
 2780         BG_WARNING=""
 2781 
 2782         # Semantic names
 2783         BAD=""
 2784         BOLD=""
 2785         HEADER=""
 2786         NORMAL=""
 2787         NOTICE=""
 2788         OK=""
 2789         WARNING=""
 2790         SECTION=""
 2791     }
 2792 
 2793     ################################################################################
 2794     # Name        : RemovePIDFile()
 2795     # Description : When defined, remove the file storing the process ID
 2796     #
 2797     # Parameters  : <none>
 2798     # Returns     : <nothing>
 2799     ################################################################################
 2800 
 2801     # Remove PID file
 2802     RemovePIDFile() {
 2803         # Test if PIDFILE is defined, before checking file presence
 2804         if [ -n "${PIDFILE}" ]; then
 2805             if [ -f "${PIDFILE}" ]; then
 2806                 rm -f "${PIDFILE}"
 2807                 LogText "PID file removed (${PIDFILE})"
 2808             else
 2809                 LogText "PID file not found (${PIDFILE})"
 2810             fi
 2811         fi
 2812     }
 2813 
 2814 
 2815     ################################################################################
 2816     # Name        : RemoveTempFiles()
 2817     # Description : When created, delete any temporary file
 2818     ################################################################################
 2819 
 2820     # Remove any temporary files
 2821     RemoveTempFiles() {
 2822         if [ ! "${TEMP_FILES}" = "" ]; then
 2823             LogText "Temporary files: ${TEMP_FILES}"
 2824             # Clean up temp files
 2825             for FILE in ${TEMP_FILES}; do
 2826                 # Temporary files should be in /tmp
 2827                 TMPFILE=$(echo ${FILE} | grep -E "^/tmp/lynis" | grep -v "\.\.")
 2828                 if [ -n "${TMPFILE}" ]; then
 2829                     if [ -f "${TMPFILE}" ]; then
 2830                         LogText "Action: removing temporary file ${TMPFILE}"
 2831                         rm -f "${TMPFILE}"
 2832                     else
 2833                         LogText "Info: temporary file ${TMPFILE} was already removed"
 2834                     fi
 2835                 else
 2836                     LogText "Found invalid temporary file (${FILE}), not removed. Check your /tmp directory."
 2837                 fi
 2838             done
 2839         else
 2840             LogText "No temporary files to be deleted"
 2841         fi
 2842       }
 2843 
 2844 
 2845     ################################################################################
 2846     # Name        : Report()
 2847     # Description : Store data in the report file
 2848     #
 2849     # Parameters  : $1 = data (format: key=value)
 2850     # Returns     : <nothing>
 2851     ################################################################################
 2852 
 2853     Report() {
 2854         if [ ${CREATE_REPORT_FILE} -eq 1 ]; then echo "$1" >> ${REPORTFILE}; fi
 2855     }
 2856 
 2857 
 2858     ################################################################################
 2859     # Name        : ReportDetails()
 2860     # Description : Adds specific details to the report, in particular when many
 2861     #               smaller atomic tests are performed. For example sysctl keys,
 2862     #               and SSH settings.
 2863     #
 2864     # Parameters  : <multiple fields, see test>
 2865     # Returns     : <nothing>
 2866     # Notes       : Need to check for values (strip out semicolons from values)
 2867     #               Only add fields which are filled in
 2868     ################################################################################
 2869 
 2870     ReportDetails() {
 2871         TEST_DESCRIPTION=""
 2872         TEST_FIELD=""
 2873         TEST_ID=""
 2874         TEST_OTHER=""
 2875         TEST_PREFERRED_VALUE=""
 2876         TEST_SERVICE=""
 2877         TEST_VALUE=""
 2878         while [ $# -ge 1 ]; do
 2879 
 2880             case $1 in
 2881                 --description)
 2882                     shift
 2883                     TEST_DESCRIPTION="desc:$1;"
 2884                 ;;
 2885                 --field)
 2886                     shift
 2887                     TEST_FIELD="field:$1;"
 2888                 ;;
 2889                 --preferredvalue|--preferred-value)
 2890                     shift
 2891                     TEST_PREFERRED_VALUE="prefval:$1;"
 2892                 ;;
 2893                 # Other details
 2894                 --other)
 2895                     shift
 2896                     TEST_OTHER="other:$1;"
 2897                 ;;
 2898                 --service)
 2899                     shift
 2900                     TEST_SERVICE="$1"
 2901                 ;;
 2902                 --test)
 2903                     shift
 2904                     TEST_ID=$1
 2905                 ;;
 2906                 --value)
 2907                     shift
 2908                     TEST_VALUE="value:$1;"
 2909                 ;;
 2910                 *)
 2911                     echo "INVALID OPTION (ReportDetails): $1"
 2912                     ExitFatal
 2913                 ;;
 2914             esac
 2915             shift # Go to next parameter
 2916         done
 2917         if [ "${TEST_ID}" = "" ]; then TEST_ID="-"; fi
 2918         if [ "${TEST_SERVICE}" = "" ]; then TEST_SERVICE="-"; fi
 2919         Report "details[]=${TEST_ID}|${TEST_SERVICE}|${TEST_DESCRIPTION}${TEST_FIELD}${TEST_PREFERRED_VALUE}${TEST_VALUE}${TEST_OTHER}|"
 2920     }
 2921 
 2922 
 2923     ################################################################################
 2924     # Name        : ReportException()
 2925     # Description : Store an exceptional event in the report
 2926     #
 2927     # Parameters  : $1 = test ID + colon + 2 numeric characters (TEST-1234:01)
 2928     #               $2 = string (text)
 2929     # Notes       : Allow this function with only 1 parameter in strict mode with ${2-Text}
 2930     ################################################################################
 2931 
 2932     ReportException() {
 2933         Report "exception_event[]=$1|${2-NoInfo}|"
 2934         LogText "Exception: test has an exceptional event ($1) with text ${2-NoText}"
 2935         if [ ${QUIET} -eq 0 ]; then
 2936             DisplayException "$1" "$2"
 2937         fi
 2938     }
 2939 
 2940 
 2941     ################################################################################
 2942     # Name        : ReportManual()
 2943     # Description : Add an item to the report that requires manual intervention
 2944     #
 2945     # Parameters  : $1 = string (text)
 2946     ################################################################################
 2947 
 2948     ReportManual() {
 2949         Report "manual_event[]=$1"
 2950         LogText "Manual: one or more manual actions are required for further testing of this control/plugin"
 2951     }
 2952 
 2953 
 2954     ################################################################################
 2955     # Name        : ReportSuggestion()
 2956     # Description : Log a suggestion to the report file
 2957     #
 2958     # Parameters  : <ID> <Suggestion> <Details> <Solution>
 2959     #               $1 = Test ID - Lynis ID (use CUST-.... for your own tests)
 2960     #               $2 = Suggestion - Suggestion text to be displayed
 2961     #               $3 = Details - Specific item or details
 2962     #               $4 = Solution - Optional link for additional information:
 2963     #                    * url:https://example.org/how-to-solve-link
 2964     #                    * text:Additional explanation
 2965     #                    * - (dash) for none
 2966     # Returns     : <nothing>
 2967     ################################################################################
 2968 
 2969     ReportSuggestion() {
 2970         TOTAL_SUGGESTIONS=$((TOTAL_SUGGESTIONS + 1))
 2971         if [ $# -eq 0 ]; then echo "Not enough arguments provided for function ReportSuggestion"; ExitFatal; fi
 2972         if [ $# -ge 1 ]; then TEST="$1"; else TEST="UNKNOWN"; fi
 2973         if [ $# -ge 2 ]; then MESSAGE="$2"; else MESSAGE="UNKNOWN"; fi
 2974         if [ $# -ge 3 ]; then DETAILS="$3"; else DETAILS="-"; fi
 2975         if [ $# -ge 4 ]; then SOLUTION="$4"; else SOLUTION="-"; fi
 2976         if [ $# -ge 5 ]; then echo "Too many arguments for function ReportSuggestion"; ExitFatal; fi
 2977         Report "suggestion[]=${TEST}|${MESSAGE}|${DETAILS}|${SOLUTION}|"
 2978         LogText "Suggestion: ${MESSAGE} [test:${TEST}] [details:${DETAILS}] [solution:${SOLUTION}]"
 2979     }
 2980 
 2981 
 2982     ################################################################################
 2983     # Name        : ReportWarning()
 2984     # Description : Log a warning to the report file
 2985     #
 2986     # Parameters  : $1 = test ID
 2987     #               $2 = message (optional)
 2988     #               $3 = details (optional)
 2989     #               $4 = possible solution (optional)
 2990     # Returns     : <nothing>
 2991     ################################################################################
 2992 
 2993     ReportWarning() {
 2994         TOTAL_WARNINGS=$((TOTAL_WARNINGS + 1))
 2995         # Old style used L/M/H level as second parameter. This is deprecated as of 3.x
 2996         if [ "$2" = "L" -o "$2" = "M" -o "$2" = "H" ]; then
 2997             ExitFatal "Deprecated usage of ReportWarning() function"
 2998         else
 2999             # New style warning format:
 3000             # <ID> <Warning> <Details> <Solution>
 3001             #
 3002             # <ID>         Lynis ID (use CUST-.... for your own tests)
 3003             # <Warning>    Warning text to be displayed
 3004             # <Details>    Specific item or details
 3005             # <Solution>   Optional link for additional information:
 3006             #              * url:http://site/link
 3007             #              * text:Additional explanation
 3008             #              * - for none
 3009             if [ $# -eq 0 ]; then echo "Not enough arguments provided for function ReportWarning"; ExitFatal; fi
 3010             if [ $# -ge 1 ]; then TEST="$1"; else TEST="UNKNOWN"; fi
 3011             if [ $# -ge 2 ]; then MESSAGE="$2"; else MESSAGE="UNKNOWN"; fi
 3012             if [ $# -ge 3 ]; then DETAILS="$3"; else DETAILS="-"; fi
 3013             if [ $# -ge 4 ]; then SOLUTION="$4"; else SOLUTION="-"; fi
 3014             if [ $# -ge 5 ]; then echo "Too many arguments for function ReportWarning"; ExitFatal; fi
 3015         fi
 3016         Report "warning[]=${TEST}|${MESSAGE}|${DETAILS}|${SOLUTION}|"
 3017         LogText "Warning: ${MESSAGE} [test:${TEST}] [details:${DETAILS}] [solution:${SOLUTION}]"
 3018     }
 3019 
 3020 
 3021     ################################################################################
 3022     # Name        : SafeInput()
 3023     # Description : Test provided string to see if it contains unwanted characters
 3024     #
 3025     # Input       : string + optional class (parameter 2)
 3026     # Returns     : exit code (0 = input considered to be safe, 1 = validation failed)
 3027     ################################################################################
 3028 
 3029     SafeInput() {
 3030         exitcode=1
 3031         # By default remove only control characters
 3032         if [ $# -eq 1 ]; then
 3033             input="$1"
 3034             cleaned=$(echo ${input} | tr -d '[:cntrl:]')
 3035         # If know what to test against, then see if input matches the specified class
 3036         elif [ $# -eq 2 ]; then
 3037             input="$1"
 3038             testchars="$2"
 3039             cleaned=$(echo $1 | tr -cd "${testchars}")
 3040         else
 3041             ExitFatal "No argument or too many arguments provided to SafeInput()"
 3042         fi
 3043 
 3044         if [ "${cleaned}" = "${input}" ]; then
 3045             exitcode=0
 3046         fi
 3047         return ${exitcode}
 3048     }
 3049 
 3050 
 3051     ################################################################################
 3052     # Name        : SafeFile()
 3053     # Description : Check if a file is safe to use
 3054     #
 3055     # Parameters  : $1 = file name
 3056     # Returns     : exit code (0 = OK, 1 = issue)
 3057     ################################################################################
 3058 
 3059     SafeFile() {
 3060         unsafe=0
 3061         if [ $# -ne 1 ]; then
 3062             ExitFatal "No argument or too many arguments provided to SafeFile()"
 3063         else
 3064             FILE="$1"
 3065 
 3066             # Generic checks
 3067             if [ -g "${FILE}" ]; then
 3068                 LogText "Security alert: file has setgid attribute"
 3069                 unsafe=1
 3070             # sticky bit
 3071             elif [ -k "${FILE}" ]; then
 3072                 LogText "Security alert: file has sticky bit"
 3073                 unsafe=1
 3074             # symbolic link
 3075             elif [ -L "${FILE}" ]; then
 3076                 LogText "Security alert: file is a symbolic link"
 3077                 unsafe=1
 3078             elif [ -f "${FILE}" ]; then
 3079                 LogText "Security check: file is normal"
 3080             else
 3081                 unsafe=1
 3082             fi
 3083 
 3084             # Perform additional checks based on privilege level
 3085             if [ ${PRIVILEGED} -eq 0 ]; then
 3086                 # File is not owned by active user, but still able to write
 3087                 if [ ! -O "${FILE}" -a -w "${FILE}" ]; then
 3088                     unsafe=1
 3089                     LogText "Security alert: file is not owned by active user, but can write to it"
 3090                 fi
 3091             fi
 3092 
 3093             # Check file permissions
 3094             if ! SafePerms "${FILE}"; then
 3095                 unsafe=1
 3096             fi
 3097 
 3098         fi
 3099 
 3100         return ${unsafe}
 3101     }
 3102 
 3103 
 3104     ################################################################################
 3105     # Name        : SafePerms()
 3106     # Description : Check if a file has safe permissions to be used
 3107     #
 3108     # Parameters  : $1 = file name
 3109     #               $2 = type of file (optional)
 3110     # Returns     : 0 (file permissions OK) or break
 3111     ################################################################################
 3112 
 3113     SafePerms() {
 3114         exitcode=1
 3115         IS_PARAMETERS=0
 3116         IS_PROFILE=0
 3117 
 3118         if [ ${IGNORE_FILE_PERMISSION_ISSUES} -eq 0 ]; then
 3119             PERMS_OK=0
 3120             LogText "Checking permissions of $1"
 3121 
 3122             if [ $# -gt 0 ]; then
 3123 
 3124                 if [ $# -eq 2 ]; then
 3125                     case "$2" in
 3126                         "parameters")
 3127                             IS_PARAMETERS=1
 3128                         ;;
 3129                         "profile")
 3130                             IS_PROFILE=1
 3131                         ;;
 3132                     esac
 3133                 else
 3134                     FIND=$(echo $1 | grep "/parameters")
 3135                     if [ $? -eq 0 ]; then IS_PARAMETERS=1; fi
 3136                 fi
 3137                 # Check file permissions
 3138                 if [ ! -f "$1" ]; then
 3139                     LogText "Fatal error: file $1 does not exist."
 3140                     ExitFatal "Fatal error: file $1 does not exist"
 3141                 else
 3142                     PERMS=$(ls -l $1)
 3143 
 3144                     # Owner permissions
 3145                     OWNER=$(echo ${PERMS} | awk -F" " '{ print $3 }')
 3146                     OWNERID=$(ls -n $1 | awk -F" " '{ print $3 }')
 3147                     if [ ${PENTESTINGMODE} -eq 0 -a ${IS_PARAMETERS} -eq 0 ]; then
 3148                         if [ ! "${OWNER}" = "root" -a ! "${OWNERID}" = "0" ]; then
 3149                             echo "Fatal error: file $1 should be owned by user 'root' when running it as root (found: ${OWNER})."
 3150                             ExitFatal
 3151                         fi
 3152                     fi
 3153                     # Group permissions
 3154                     GROUP=$(echo ${PERMS} | awk -F" " '{ print $4 }')
 3155                     GROUPID=$(ls -n $1 | awk -F" " '{ print $4 }')
 3156 
 3157                     if [ ${PENTESTINGMODE} -eq 0 -a ${IS_PARAMETERS} -eq 0 ]; then
 3158                         if [ ! "${GROUP}" = "root" -a ! "${GROUP}" = "wheel" -a ! "${GROUPID}" = "0" ]; then
 3159                             echo "Fatal error: group owner of directory $1 should be owned by root user, wheel or similar (found: ${GROUP})."
 3160                             ExitFatal
 3161                         fi
 3162                     fi
 3163 
 3164                     # Owner permissions
 3165                     OWNER_PERMS=$(echo ${PERMS} | cut -c2-4)
 3166                     if [ ! "${OWNER_PERMS}" = "rw-" -a ! "${OWNER_PERMS}" = "r--" ]; then
 3167                         echo "Fatal error: permissions of file $1 are not strict enough. Access to 'owner' should be read-write, or read. Change with: chmod u=rw $1"
 3168                         ExitFatal
 3169                     fi
 3170 
 3171                     # Group permissions
 3172                     # TODO - harden this even more by setting default to read-only for group (like 'other')
 3173                     GROUP_PERMS=$(echo ${PERMS} | cut -c5-7)
 3174                     if [ ! "${GROUP_PERMS}" = "rw-" -a ! "${GROUP_PERMS}" = "r--" -a ! "${GROUP_PERMS}" = "---" ]; then
 3175                         echo "Fatal error: permissions of file $1 are not strict enough. Access to 'group' should be read-write, read, or none. Change with: chmod g=r $1"
 3176                         ExitFatal
 3177                     fi
 3178 
 3179                     # Other permissions
 3180                     OTHER_PERMS=$(echo ${PERMS} | cut -c8-10)
 3181                     if [ ! "${OTHER_PERMS}" = "---" -a ! "${OTHER_PERMS}" = "r--" ]; then
 3182                         echo "Fatal error: permissions of file $1 are not strict enough. Access to 'other' should be denied or read-only. Change with: chmod o=r $1"
 3183                         ExitFatal
 3184                     fi
 3185                     # Set PERMS_OK to 1 if no fatal errors occurred
 3186                     PERMS_OK=1
 3187                     LogText "File permissions are OK"
 3188                     exitcode=0
 3189                 fi
 3190             else
 3191                 ReportException "SafePerms()" "Invalid number of arguments for function"
 3192             fi
 3193         else
 3194             PERMS_OK=1
 3195             exitcode=0
 3196         fi
 3197         return ${exitcode}
 3198 
 3199     }
 3200 
 3201 
 3202     ################################################################################
 3203     # Name        : SearchItem()
 3204     # Description : Search if a specific string exists in in a file
 3205     #
 3206     # Parameters  : $1 = search key (string)
 3207     #               $2 = file (string)
 3208     #               $3 = optional arguments:
 3209     #                    --sensitive - don't store results in log
 3210     # Returns     : True (0) or False (1)
 3211     ################################################################################
 3212 
 3213     SearchItem()  {
 3214         PERFORM_SCAN=0
 3215         MASK_LOG=0
 3216         RETVAL=1
 3217         if [ $# -lt 2 ]; then
 3218             ExitFatal "Not enough arguments for function SearchItem()"
 3219         elif [ $# -ge 2 ]; then
 3220             FILE=$2
 3221             STRING=$1
 3222             PERFORM_SCAN=1
 3223         fi
 3224 
 3225         # Parse any additional arguments
 3226         if [ $# -gt 2 ]; then
 3227             shift; shift # Skip the first two (string and file)
 3228             while [ $# -ge 1 ]; do
 3229                 case $1 in
 3230                     "--sensitive") MASK_LOG=1 ;;
 3231                 esac
 3232                 shift # Go to next parameter
 3233             done
 3234         fi
 3235 
 3236         if [ ${PERFORM_SCAN} -eq 1 ]; then
 3237             # Don't search in /dev/null, it's too empty there
 3238             if [ -f ${FILE} ]; then
 3239                 # Check if we can find the main type (with or without brackets)
 3240                 LogText "Test: search string ${STRING} in file ${FILE}"
 3241                 FIND=$(grep -E "${STRING}" ${FILE})
 3242                 if [ -n "${FIND}" ]; then
 3243                     LogText "Result: found search string '${STRING}'"
 3244                     if [ ${MASK_LOG} -eq 0 ]; then LogText "Full string returned: ${FIND}"; fi
 3245                     RETVAL=0
 3246                 else
 3247                     LogText "Result: search search string '${STRING}' NOT found"
 3248                     RETVAL=1
 3249                 fi
 3250             else
 3251                 LogText "Skipping search, file (${FILE}) does not exist"
 3252                 ReportException "${TEST_NO}" "Test is trying to search for a string in nonexistent file"
 3253             fi
 3254         else
 3255             ReportException "${TEST_NO}" "Search test is skipped, which is unexpected"
 3256         fi
 3257         return ${RETVAL}
 3258     }
 3259 
 3260 
 3261 
 3262 
 3263     ################################################################################
 3264     # Name        : ShowComplianceFinding()
 3265     # Description : Display a section of a compliance standard which is not fulfilled
 3266     #
 3267     # Parameters  : <several parameters, see test>
 3268     # Returns     : <nothing>
 3269     ################################################################################
 3270 
 3271     ShowComplianceFinding() {
 3272         REASON=""
 3273         STANDARD_NAME=""
 3274         STANDARD_VERSION=""
 3275         STANDARD_SECTION=""
 3276         STANDARD_SECTION_TITLE=""
 3277         ACTUAL_VALUE=""
 3278         EXPECTED_VALUE=""
 3279         while [ $# -ge 1 ]; do
 3280             case $1 in
 3281                 --standard)
 3282                     shift
 3283                     STANDARD_NAME=$1
 3284                 ;;
 3285                 --version)
 3286                     shift
 3287                     STANDARD_VERSION=$1
 3288                 ;;
 3289                 --section)
 3290                     shift
 3291                     STANDARD_SECTION=$1
 3292                 ;;
 3293                 --section-title)
 3294                     shift
 3295                     STANDARD_SECTION_TITLE=$1
 3296                 ;;
 3297                 --reason)
 3298                     shift
 3299                     REASON=$1
 3300                 ;;
 3301                 --actual)
 3302                     shift
 3303                     ACTUAL_VALUE=$1
 3304                 ;;
 3305                 --expected)
 3306                     shift
 3307                     EXPECTED_VALUE=$1
 3308                 ;;
 3309 
 3310                 *)
 3311                     echo "INVALID OPTION (ShowComplianceFinding): $1"
 3312                     exit 1
 3313                 ;;
 3314             esac
 3315             # Go to next parameter
 3316             shift
 3317         done
 3318         # Should we show this non-compliance on screen?
 3319         SHOW=0
 3320         case ${STANDARD_NAME} in
 3321             cis)
 3322                 if [ ${COMPLIANCE_ENABLE_CIS} -eq 1 ]; then SHOW=1; fi
 3323                 STANDARD_FRIENDLY_NAME="CIS"
 3324             ;;
 3325             hipaa)
 3326                 if [ ${COMPLIANCE_ENABLE_HIPAA} -eq 1 ]; then SHOW=1; fi
 3327                 STANDARD_FRIENDLY_NAME="HIPAA"
 3328             ;;
 3329             iso27001)
 3330                 if [ ${COMPLIANCE_ENABLE_ISO27001} -eq 1 ]; then SHOW=1; fi
 3331                 STANDARD_FRIENDLY_NAME="ISO27001"
 3332             ;;
 3333             pci-dss)
 3334                 if [ ${COMPLIANCE_ENABLE_PCI_DSS} -eq 1 ]; then SHOW=1; fi
 3335                 STANDARD_FRIENDLY_NAME="PCI DSS"
 3336             ;;
 3337         esac
 3338         # Only display if standard is enabled in the profile and mark system as non-compliant
 3339         if [ ${SHOW} -eq 1 ]; then
 3340             COMPLIANCE_FINDINGS_FOUND=1
 3341             DisplayManual "  [${WHITE}${STANDARD_FRIENDLY_NAME} ${STANDARD_VERSION}${NORMAL}] - ${CYAN}Section ${STANDARD_SECTION}${NORMAL} - ${WHITE}${STANDARD_SECTION_TITLE}${NORMAL}"
 3342             DisplayManual "  - Details:        ${REASON}"
 3343             DisplayManual "  - Configuration:  ${RED}${ACTUAL_VALUE}${NORMAL} / ${EXPECTED_VALUE}"
 3344             DisplayManual ""
 3345         fi
 3346     }
 3347 
 3348 
 3349     ################################################################################
 3350     # Name        : ShowSymlinkPath()
 3351     # Description : Check if we can find the path behind a symlink
 3352     #
 3353     # Parameters  : $1 = file (string)
 3354     # Returns     : FOUNDPATH (0 not found, 1 found path), SYMLINK (new path)
 3355     ################################################################################
 3356 
 3357     ShowSymlinkPath() {
 3358         if [ $# -eq 0 ]; then ExitFatal "Function ShowSymlinkPath() called without a file name"; fi
 3359         sFILE=$1
 3360         FOUNDPATH=0
 3361         SYMLINK_USE_PYTHON=0
 3362         SYMLINK_USE_READLINK=0
 3363         LogText "Action: checking symlink for file ${sFILE}"
 3364         # Check for symlink
 3365         if [ -L ${sFILE} ]; then
 3366 
 3367             # macOS does not know -f option, nor do some others
 3368             if [ "${OS}" = "macOS" ]; then
 3369                 # If a Python binary is found, use the one in path
 3370                 if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${PYTHONBINARY}" = "" ]; then
 3371                     FIND=$(which python 2> /dev/null | grep -v "no [^ ]* in ")
 3372                     if [ ! "${FIND}" = "" ]; then LogText "Setting temporary pythonbinary variable"; PYTHONBINARY="${FIND}"; fi
 3373                 fi
 3374 
 3375                 if [ ! "${PYTHONBINARY}" = "" ]; then
 3376                     SYMLINK_USE_PYTHON=1
 3377                     LogText "Note: using Python to determine symlinks"
 3378                     tFILE=$(python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" $1)
 3379                 fi
 3380             else
 3381                 if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${READLINKBINARY}" = "" ]; then
 3382                     FIND=$(which readlink 2> /dev/null | grep -v "no [^ ]* in ")
 3383                     if [ ! "${FIND}" = "" ]; then LogText "Setting temporary readlinkbinary variable"; READLINKBINARY="${FIND}"; fi
 3384                 fi
 3385 
 3386                 if [ ! "${READLINKBINARY}" = "" ]; then
 3387                     SYMLINK_USE_READLINK=1
 3388                     LogText "Note: Using real readlink binary to determine symlink on ${sFILE}"
 3389                     tFILE=$(${READLINKBINARY} -f ${sFILE})
 3390                     LogText "Result: readlink shows ${tFILE} as output"
 3391                 fi
 3392             fi
 3393             # Check if we can find the file now
 3394             if [ "${tFILE}" = "" ]; then
 3395                 LogText "Result: command did not return any value"
 3396             elif [ -f ${tFILE} ]; then
 3397                 sFILE="${tFILE}"
 3398                 LogText "Result: symlink found, pointing to file ${sFILE}"
 3399                 FOUNDPATH=1
 3400             elif [ -b ${tFILE} ]; then
 3401                 sFILE="${tFILE}"
 3402                 LogText "Result: symlink found, pointing to block device ${sFILE}"
 3403                 FOUNDPATH=1
 3404             elif [ -c ${tFILE} ]; then
 3405                 sFILE="${tFILE}"
 3406                 LogText "Result: symlink found, pointing to character device ${sFILE}"
 3407                 FOUNDPATH=1
 3408             elif [ -d ${tFILE} ]; then
 3409                 sFILE="${tFILE}"
 3410                 LogText "Result: symlink found, pointing to directory ${sFILE}"
 3411                 FOUNDPATH=1
 3412             else
 3413                 # Check the full path of the symlink, strip the filename, copy the path and linked filename together
 3414                 tDIR=$(echo ${sFILE} | awk '{match($1, "^.*/"); print substr($1, 1, RLENGTH-1)}')
 3415                 tFILE="${tDIR}/${tFILE}"
 3416                 if [ -L ${tFILE} ]; then
 3417                     LogText "Result: this symlink links to another symlink"
 3418                     # Ensure that we use a second try with the right tool as well
 3419                     if [ ${SYMLINK_USE_PYTHON} -eq 1 ]; then
 3420                         tFILE=$(python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" ${tFILE})
 3421                     elif [ ${SYMLINK_USE_READLINK} -eq 1 ]; then
 3422                         tFILE=$(${READLINKBINARY} -f ${tFILE})
 3423                     fi
 3424                     # Check if we now have a normal file
 3425                     if [ -f ${tFILE} ]; then
 3426                         sFILE="${tFILE}"
 3427                         LogText "Result: symlink finally found, seems to be file ${sFILE}"
 3428                         FOUNDPATH=1
 3429                     elif [ -d ${tFILE} ]; then
 3430                         sFILE="${tFILE}"
 3431                         LogText "Result: symlink finally found, seems to be directory ${sFILE}"
 3432                         FOUNDPATH=1
 3433                     else
 3434                        LogText "Result: could not find file ${tFILE}, most likely too complicated symlink or too often linked"
 3435                     fi
 3436                 elif [ -f ${tFILE} ]; then
 3437                     sFILE="${tFILE}"
 3438                     LogText "Result: symlink found, seems to be file ${sFILE}"
 3439                     FOUNDPATH=1
 3440                 elif [ -d ${tFILE} ]; then
 3441                     sFILE="${tFILE}"
 3442                     LogText "Result: symlink found, seems to be directory ${sFILE}"
 3443                     FOUNDPATH=1
 3444                 else
 3445                     LogText "Result: file ${tFILE} in ${tDIR} not found"
 3446                 fi
 3447             fi
 3448         else
 3449             LogText "Result: file ${sFILE} is not a symlink"
 3450         fi
 3451         # Now check if our new location is actually a file or directory destination
 3452         if [ -L ${sFILE} ]; then
 3453             LogText "Result: unable to determine symlink, or location ${sFILE} is just another symlink"
 3454             FOUNDPATH=0
 3455         fi
 3456         if [ ${FOUNDPATH} -eq 1 ]; then
 3457             SYMLINK="${sFILE}"
 3458         else
 3459             SYMLINK=""
 3460         fi
 3461     }
 3462 
 3463 
 3464     ################################################################################
 3465     # Name        : SkipAtomicTest()
 3466     # Description : Test if an atomic test should be skipped
 3467     #
 3468     # Input       : String (particular test)
 3469     # Returns     : 0 (skip test) or 1 (do not skip test)
 3470     # Usage       : if SkipAtomicTest "ssh-7408:port"; then echo "Skip this atomic test"; fi
 3471     ################################################################################
 3472 
 3473     SkipAtomicTest() {
 3474         RETVAL=255
 3475         if [ $# -eq 1 ]; then
 3476             STRING=""
 3477             RETVAL=1
 3478             # Check if this test is on the list to skip
 3479             for item in ${SKIP_TESTS}; do
 3480                 STRING=$(echo $1 | tr '[:lower:]' '[:upper:]')
 3481                 if [ "${item}" = "${STRING}" ]; then RETVAL=0; LogText "Atomic test ($1) skipped by configuration (skip-test)"; fi
 3482             done
 3483         else
 3484             ReportException "SkipAtomicTest()" "Function called without right number of arguments (1)"
 3485         fi
 3486         return $RETVAL
 3487     }
 3488 
 3489 
 3490     ################################################################################
 3491     # Name        : Status()
 3492     # Description : Reports back the status of tool
 3493     #
 3494     # Returns     : text to screen
 3495     # Notes       : kill --signal USR1 <PID> or pkill --signal USR1 lynis
 3496     ################################################################################
 3497 
 3498     Status() {
 3499         echo ""
 3500         echo "Date / time : $(date "+%Y-%m-%d %H:%M:%S")"
 3501         echo "Active test : ${TEST_NO:-NONE}"
 3502         echo ""
 3503     }
 3504 
 3505 
 3506     ################################################################################
 3507     # Name        : StoreNginxSettings()
 3508     # Description : Store parsed settings from nginx (by ParseNginx)
 3509     # Input       : multiple options
 3510     # Returns     : <nothing>
 3511     ################################################################################
 3512 
 3513     StoreNginxSettings() {
 3514         CONFIG_DEPTH=0; CONFIG_FILE=""; CONFIG_SETTING=""; CONFIG_TREE=""; CONFIG_VALUE=""
 3515         while [ $# -ge 1 ]; do
 3516             case $1 in
 3517                 --config)
 3518                     shift
 3519                     CONFIG_FILE=$1
 3520                 ;;
 3521                 --depth)
 3522                     shift
 3523                     CONFIG_DEPTH=$1
 3524                 ;;
 3525                 # none | events | server | unknown
 3526                 --tree)
 3527                     shift
 3528                     CONFIG_TREE=$1
 3529                     case ${CONFIG_TREE} in
 3530                         "/") CONFIG_COUNTER=0 ;;
 3531                         "/events") CONFIG_COUNTER=${NGINX_EVENTS_COUNTER=0} ;;
 3532                         "/http") CONFIG_COUNTER=${NGINX_HTTP_COUNTER=0} ;;
 3533                         "/server") CONFIG_COUNTER=${NGINX_SERVER_COUNTER=0} ;;
 3534                         "/server/location") CONFIG_COUNTER=${NGINX_LOCATION_COUNTER=0} ;;
 3535                         *)
 3536                         Debug "Unknown configuration tree of nginx ${CONFIG_TREE}"
 3537                         ;;
 3538                     esac
 3539                 ;;
 3540                 --setting)
 3541                     shift
 3542                     CONFIG_SETTING=$1
 3543                 ;;
 3544                 --value)
 3545                     shift
 3546                     CONFIG_VALUE=$1
 3547                 ;;
 3548                 *)
 3549                     echo "INVALID OPTION (StoreNginxSettings): $1 $2"
 3550                     #ExitFatal
 3551                 ;;
 3552             esac
 3553             # Go to next parameter
 3554             shift
 3555         done
 3556         if [ -z "${CONFIG_DEPTH}" ]; then CONFIG_DEPTH="0"; fi
 3557         if [ -z "${CONFIG_SETTING}" ]; then CONFIG_SETTING="NA"; fi
 3558         if [ -z "${CONFIG_TREE}" ]; then CONFIG_TREE="/"; fi
 3559         if [ -z "${CONFIG_VALUE}" ]; then CONFIG_VALUE="NA"; fi
 3560         Report "nginx_config[]=|file=${CONFIG_FILE}|depth=${CONFIG_DEPTH}|tree=${CONFIG_TREE}|number=${CONFIG_COUNTER}|setting=${CONFIG_SETTING}|value=${CONFIG_VALUE}|"
 3561     }
 3562 
 3563 
 3564     ################################################################################
 3565     # Name        : TestValue()
 3566     # Description : Test if a value is good/bad (e.g. according to best practices)
 3567     #
 3568     # Input       : --function <value> --value <value> --search <value>
 3569     # Returns     : 0 (True) or 1 (False)
 3570     # Usage       : if TestValue --function contains --value "Full Text" --search "Text"; then echo "Found!"; fi
 3571     ################################################################################
 3572 
 3573     TestValue() {
 3574         RETVAL=255
 3575         FIND=""
 3576         VALUE=""
 3577         RESULT=""
 3578         SEARCH=""
 3579         CMP1=""; CMP2=""
 3580         while [ $# -ge 1 ]; do
 3581             case $1 in
 3582                 --function)
 3583                     shift
 3584                     FUNCTION=$1
 3585                 ;;
 3586                 --value)
 3587                     shift
 3588                     VALUE=$1
 3589                 ;;
 3590                 --search)
 3591                     shift
 3592                     SEARCH=$1
 3593                 ;;
 3594                 *)
 3595                     echo "INVALID OPTION USED (TestValue): $1"
 3596                     exit 1
 3597                 ;;
 3598             esac
 3599             # Go to next parameter
 3600             shift
 3601         done
 3602 
 3603         if [ "${VALUE}" = "" ]; then echo "No value provided to function (TestValue)"; fi
 3604 
 3605         # Apply the related function
 3606         case ${FUNCTION} in
 3607               "contains")
 3608                   FIND=$(echo ${VALUE} | grep -E "${SEARCH}")
 3609                   if [ "${FIND}" = "" ]; then RETVAL=1; else RETVAL=0; fi
 3610               ;;
 3611               #"gt" | "greater-than")   COLOR=$GREEN   ;;
 3612               "equals")
 3613                   CMP1=$(echo ${SEARCH} | tr '[:upper:]' '[:lower:'])
 3614                   CMP2=$(echo ${VALUE} | tr '[:upper:]' '[:lower:'])
 3615                   if [ "${CMP1}" = "${CMP2}" ]; then RETVAL=0; else RETVAL=1; fi
 3616               ;;
 3617               #"not-equal")   COLOR=$WHITE   ;;
 3618               #"lt" | "less-than")  COLOR=$YELLOW  ;;
 3619               *) echo "INVALID OPTION USED (TestValue, parameter of function: $1)"; exit 1 ;;
 3620         esac
 3621 
 3622         if [ "${RETVAL}" -lt 2 ]; then
 3623             return ${RESULT}
 3624         else
 3625             Fatal "ERROR: No result returned from function (TestValue). Incorrect usage?"
 3626             #ExitFatal
 3627         fi
 3628     }
 3629 
 3630 
 3631     ################################################################################
 3632     # Name        : ViewCategories()
 3633     # Description : Show all available categories
 3634     #
 3635     # Input       : <nothing>
 3636     # Returns     : <nothing>
 3637     # Usage       : ViewCategories
 3638     ################################################################################
 3639 
 3640     ViewCategories() {
 3641         for CATEGORY in ${TEST_AVAILABLE_CATEGORIES}; do echo "${CATEGORY}"; done
 3642         ExitClean
 3643     }
 3644 
 3645 
 3646     ################################################################################
 3647     # Name        : ViewGroups()
 3648     # Description : Show what group of tests are available
 3649     #
 3650     # Input       : <nothing>
 3651     # Returns     : <nothing>
 3652     # Usage       : ViewGroups
 3653     ################################################################################
 3654 
 3655     ViewGroups() {
 3656         if [ -n "${INCLUDEDIR}" ]; then
 3657             for I in $(ls ${INCLUDEDIR}/tests_* | xargs -n 1 basename | sed 's/tests_//' | grep -v "custom.template"); do
 3658               echo "${I}"
 3659             done
 3660         fi
 3661         ExitClean
 3662     }
 3663 
 3664 
 3665     ################################################################################
 3666     # Name        : WaitForKeyPress()
 3667     # Description : Wait for the user to press [ENTER], unless quickmode is set
 3668     #
 3669     # Input       : <nothing>
 3670     # Returns     : <nothing>
 3671     # Usage       : WaitForKeyPress
 3672     ################################################################################
 3673 
 3674     WaitForKeyPress() {
 3675         if [ ${QUICKMODE} -eq 0 ]; then
 3676             echo ""; echo "[ Press [ENTER] to continue, or [CTRL]+C to stop ]"
 3677             read -r void
 3678         fi
 3679     }
 3680 
 3681 
 3682     ################################################################################
 3683     #
 3684     # Legacy functions - Do not use!
 3685     #
 3686     ################################################################################
 3687     # For compatibility reasons they are listed here, but will be phased out in
 3688     # steps. If they still get used, they will trigger errors on screen.
 3689     ################################################################################
 3690 
 3691     counttests() {
 3692         DisplayWarning "Deprecated function used (counttests)"
 3693         if IsDeveloperMode; then Debug "Warning: old counttests function is used. Please replace any reference with CountTests."; fi
 3694         CountTests
 3695     }
 3696 
 3697      logtext() {
 3698         DisplayWarning "Deprecated function used (logtext)"
 3699         if IsDeveloperMode; then Debug "Warning: old logtext function is used. Please replace any reference with LogText."; fi
 3700         LogText "$1"
 3701     }
 3702 
 3703      logtextbreak() {
 3704         DisplayWarning "Deprecated function used (logtextbreak)"
 3705         if IsDeveloperMode; then Debug "Warning: old logtextbreak function is used. Please replace any reference with LogTextBreak."; fi
 3706         LogTextBreak "$1"
 3707     }
 3708 
 3709      report() {
 3710         DisplayWarning "Deprecated function used (report)"
 3711         if IsDeveloperMode; then Debug "Warning: old report function is used. Please replace any reference with Report."; fi
 3712         Report "$1"
 3713     }
 3714 
 3715      wait_for_keypress() {
 3716         DisplayWarning "Deprecated function used (wait_for_keypress)"
 3717         if IsDeveloperMode; then Debug "Warning: old wait_for_keypress function is used. Please replace any reference with WaitForKeyPress."; fi
 3718         WaitForKeyPress
 3719     }
 3720 
 3721      ShowResult() {
 3722         DisplayWarning "Deprecated function used (ShowResult)"
 3723         if IsDeveloperMode; then Debug "Warning: old ShowResult() function is used. Please replace any reference with WaitForKeyPress."; fi
 3724     }
 3725 
 3726 
 3727 #================================================================================
 3728 # Lynis is part of Lynis Enterprise and released under GPLv3 license
 3729 # Copyright 2007-2021 - Michael Boelen, CISOfy - https://cisofy.com