mirror of https://github.com/CISOfy/lynis.git
909 lines
39 KiB
Bash
Executable File
909 lines
39 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
#################################################################################
|
|
#
|
|
# Lynis
|
|
# ------------------
|
|
#
|
|
# Copyright 2007-2016 Michael Boelen, CISOfy (michael.boelen@cisofy.com)
|
|
# Web site: https://cisofy.com
|
|
#
|
|
# Lynis comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
|
|
# welcome to redistribute it under the terms of the GNU General Public License.
|
|
# See LICENSE file for usage of this software.
|
|
#
|
|
# Lynis is licensed under GPLv3, Plugins are licensed differently (see plugins)
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Lynis is an automated auditing tool for Unix based operating systems.
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Quit when commands exit with code greater than zero (-e)
|
|
#set -e
|
|
# Strict mode (-u)
|
|
#set -u
|
|
|
|
# Program information
|
|
PROGRAM_name="Lynis"
|
|
PROGRAM_version="2.1.8"
|
|
PROGRAM_releasedate="2016-02-15"
|
|
PROGRAM_author="CISOfy"
|
|
PROGRAM_author_contact="lynis-dev@cisofy.com"
|
|
PROGRAM_website="https://cisofy.com"
|
|
PROGRAM_copyright="Copyright 2007-2016 - ${PROGRAM_author}, ${PROGRAM_website}"
|
|
PROGRAM_license="${PROGRAM_NAME} comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
|
|
welcome to redistribute it under the terms of the GNU General Public License.
|
|
See the LICENSE file for details about using this software."
|
|
PROGRAM_extrainfo="Enterprise support and plugins available via CISOfy"
|
|
# Release version (beta or final)
|
|
PROGRAM_releasetype="final"
|
|
PROGRAM_NAME="Lynis"
|
|
PROGRAM_SOURCE="https://github.com/CISOfy/lynis"
|
|
|
|
# Version number of report files (when format changes in future)
|
|
REPORT_version_major="1"; REPORT_version_minor="0"
|
|
REPORT_version="${REPORT_version_major}.${REPORT_version_minor}"
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Configure Include path and files
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Test from which directories we can use all functions and tests
|
|
|
|
INCLUDEDIR="" # Set default include directory to none
|
|
tINCLUDE_TARGETS="/usr/local/include/lynis /usr/local/lynis/include /usr/share/lynis/include ./include" # Default paths to check (CWD as last option, in case we run from standalone)
|
|
for I in ${tINCLUDE_TARGETS}; do if [ -d ${I} ]; then INCLUDEDIR=${I}; fi; done
|
|
|
|
# Drop out if our include directory can't be found
|
|
|
|
if [ "${INCLUDEDIR}" = "" ]; then
|
|
echo "Fatal error: can't find include directory"
|
|
echo "Make sure to execute ${PROGRAM_name} from untarred directory or check your installation."
|
|
exit 1
|
|
fi
|
|
|
|
# Test for database directory
|
|
|
|
DBDIR=""; tDB_TARGETS="/usr/local/share/lynis/db /usr/local/lynis/db /usr/share/lynis/db ./db"
|
|
for I in ${tDB_TARGETS}; do if [ -d ${I} ]; then DBDIR=${I}; fi; done
|
|
#
|
|
#################################################################################
|
|
#
|
|
MYID=""
|
|
# Check user to determine file permissions later on. If we encounter Solaris, use related id binary instead
|
|
if [ -x /usr/xpg4/bin/id ]; then
|
|
MYID=`/usr/xpg4/bin/id -u 2> /dev/null`
|
|
else
|
|
MYID=`id -u 2> /dev/null`
|
|
fi
|
|
if [ "${MYID}" = "" ]; then Display "Could not find user ID with id command. Want to help improving Lynis? Raise a ticket at ${PROGRAM_SOURCE}"; ExitFatal; fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Consts
|
|
# (bin paths, text strings, colors)
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Perform a basic check for permissions. After including functions, using SafePerms()
|
|
# Optimization: remove ls -l for owner and only do UID check, reducing one getpwent
|
|
PERMS=`ls -l ${INCLUDEDIR}/consts | cut -c 2-10`
|
|
PERMS2=`ls -l ${INCLUDEDIR}/functions | cut -c 2-10`
|
|
OWNER=`ls -l ${INCLUDEDIR}/consts | awk -F" " '{ print $3 }'`
|
|
OWNER2=`ls -l ${INCLUDEDIR}/functions | awk -F" " '{ print $3 }'`
|
|
OWNERID=`ls -n ${INCLUDEDIR}/consts | awk -F" " '{ print $3 }'`
|
|
OWNER2ID=`ls -n ${INCLUDEDIR}/functions | awk -F" " '{ print $3 }'`
|
|
|
|
ISSUE=0
|
|
SHOWPERMERROR=0
|
|
# Check permissions of include/consts file (400, 600, 640, 644)
|
|
if [ ! "${PERMS}" = "r--------" -a ! "${PERMS}" = "rw-------" -a ! "${PERMS}" = "rw-r-----" -a ! "${PERMS}" = "rw-r--r--" ]; then
|
|
ISSUE=1; echo "[!] Change file permissions of ${INCLUDEDIR}/consts to 640."; echo " Command: chmod 640 ${INCLUDEDIR}/consts"
|
|
fi
|
|
# Check permissions of include/functions file
|
|
if [ ! "${PERMS2}" = "r--------" -a ! "${PERMS2}" = "rw-------" -a ! "${PERMS}" = "rw-r-----" -a ! "${PERMS}" = "rw-r--r--" ]; then
|
|
ISSUE=1; echo "[!] Change file permissions of ${INCLUDEDIR}/functions to 640."; echo " Command: chmod 640 ${INCLUDEDIR}/functions"
|
|
fi
|
|
|
|
# Check if owner of both files is root user, or the same user which is running Lynis (for pentester mode)
|
|
|
|
# Consts
|
|
if [ ! "${OWNER}" = "root" -a ! "${OWNERID}" = "0" ]; then
|
|
if [ ! "${MYID}" = "${OWNER2ID}" ]; then
|
|
ISSUE=1; SHOWPERMERROR=1; FILE="consts"
|
|
fi
|
|
fi
|
|
# Functions
|
|
if [ ! "${OWNER2}" = "root" -a ! "${OWNER2ID}" = "0" ]; then
|
|
if [ ! "${MYID}" = "${OWNER2ID}" ]; then
|
|
ISSUE=1; SHOWPERMERROR=1; FILE="functions"
|
|
fi
|
|
fi
|
|
if [ ${SHOWPERMERROR} -eq 1 ]; then
|
|
echo ""
|
|
echo "[!] Change ownership of ${INCLUDEDIR}/${FILE} to 'root' or similar (found: ${OWNER} with UID ${OWNERID})."
|
|
echo ""
|
|
echo " Command:"
|
|
echo " # chown root:root ${INCLUDEDIR}/${FILE}"
|
|
echo ""
|
|
echo " Note: on some systems the default group might be 'wheel'. Use 'chown root:wheel' instead on the files."
|
|
echo ""
|
|
fi
|
|
|
|
if [ ${ISSUE} -eq 0 ]; then
|
|
. ${INCLUDEDIR}/consts
|
|
. ${INCLUDEDIR}/functions
|
|
else
|
|
echo ""; echo "";
|
|
echo "[X] Security check failed: See action above, to correct this issue."
|
|
echo ""
|
|
echo " Why do I see this error?"
|
|
echo " -------------------------------"
|
|
echo " This is a protection mechanism, to prevent the root user from executing user created files."
|
|
echo ""; echo ""
|
|
echo " What can I do?"
|
|
echo " ---------------------"
|
|
echo " 1) Check if a trusted user created the files (e.g. you, by using Git, Homebrew or similar)"
|
|
echo ""
|
|
echo " 2) Change ownership and permissions of the related files (or full directory)."
|
|
echo ""
|
|
echo " Commands (full directory):"
|
|
echo " # cd .."
|
|
echo " # chown -R root:<GROUP TO WHICH ROOT BELONGS> lynis"
|
|
echo ""
|
|
echo " 3) Start Lynis again (cd lynis && ./lynis)."
|
|
echo ""; echo "";
|
|
exit 1
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Traps
|
|
#
|
|
#################################################################################
|
|
#
|
|
trap Maid INT
|
|
|
|
# Use safe umask for the files we create
|
|
umask 027
|
|
|
|
# Drop out on unintialised variables / fatal errors
|
|
#set -u
|
|
#
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Parameter checks
|
|
#
|
|
#################################################################################
|
|
#
|
|
SafePerms ${INCLUDEDIR}/parameters
|
|
. ${INCLUDEDIR}/parameters
|
|
|
|
# Now determine if we are root (UID = 0)
|
|
if [ ${MYID} -eq 0 ]; then
|
|
PRIVILEGED=1
|
|
else
|
|
echo "Start Lynis non-privileged"; echo "";
|
|
# Implied pentesting mode if not performed by root user
|
|
PENTESTINGMODE=1
|
|
fi
|
|
|
|
# Disable logging if no alternative was provided
|
|
if [ ${PRIVILEGED} -eq 0 ]; then
|
|
if [ "${LOGFILE}" = "" ]; then
|
|
LOGFILE="/dev/null"
|
|
fi
|
|
if [ "${REPORTFILE}" = "" ]; then REPORTFILE="/dev/null"; fi
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Plugins
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Plugin directory test
|
|
if [ "${PLUGINDIR}" = "" ]; then
|
|
#logtext "Result: Searching for plugindir"
|
|
tPLUGIN_TARGETS="/usr/local/lynis/plugins /usr/local/share/lynis/plugins /usr/share/lynis/plugins /etc/lynis/plugins ./plugins"
|
|
for I in ${tPLUGIN_TARGETS}; do
|
|
if [ -d ${I} ]; then
|
|
PLUGINDIR=${I}
|
|
Debug "Result: found plugindir ${PLUGINDIR}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Drop out if our plugin directory can't be found
|
|
if [ ! -d ${PLUGINDIR} ]; then
|
|
echo "Fatal error: can't find plugin directory ${PLUGINDIR}"
|
|
echo "Make sure to execute ${PROGRAM_name} from untarred directory or check your installation."
|
|
exit 1
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Program information
|
|
#
|
|
#################################################################################
|
|
#
|
|
# CV - Current Version
|
|
PROGRAM_AC=`echo ${PROGRAM_version} | awk '{ print $1 }' | sed 's/[.]//g'`
|
|
PROGRAM_LV=0
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Initialize and default settings
|
|
#
|
|
#################################################################################
|
|
#
|
|
|
|
if [ ${QUIET} -eq 0 ]; then
|
|
echo ""
|
|
echo "${WHITE}[ ${PROGRAM_name} ${PROGRAM_version} ]${NORMAL}"
|
|
echo ""
|
|
echo "################################################################################"
|
|
echo " ${PROGRAM_license}"
|
|
echo ""
|
|
echo " ${PROGRAM_copyright}"
|
|
echo " ${PROGRAM_extrainfo}"
|
|
echo "################################################################################"
|
|
fi
|
|
|
|
if [ "${PROGRAM_releasetype}" = "beta" ]; then
|
|
echo "${WHITE}"
|
|
echo " #########################################################"
|
|
echo " # ${YELLOW}BETA VERSION${WHITE} #"
|
|
echo " #########################################################"
|
|
echo ""
|
|
echo " Thank you for testing a beta release. Make sure to read"
|
|
echo " all available documentation before proceeding and/or"
|
|
echo " requesting support. Due the nature of beta releases, it"
|
|
echo " is possible new features give unexpected warnings."
|
|
echo ""
|
|
echo ""
|
|
echo " #########################################################"
|
|
echo "${NORMAL}"; echo ""
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
InsertSection "Initializing program"
|
|
|
|
# Try to find a default profile file, if none is specified
|
|
if [ "${PROFILE}" = "" ]; then
|
|
tPROFILE_TARGETS="/usr/local/etc/lynis/default.prf /etc/lynis/default.prf /usr/local/lynis/default.prf ./default.prf"
|
|
for I in ${tPROFILE_TARGETS}; do
|
|
if [ -f ${I} ]; then PROFILE=${I}; fi
|
|
done
|
|
fi
|
|
if [ "${PROFILE}" = "" ]; then
|
|
echo "${RED}Fatal error: ${WHITE}No profile defined and could not find default profile${NORMAL}"
|
|
echo "Search paths used --> ${tPROFILE_TARGETS}"
|
|
ExitCustom 66
|
|
fi
|
|
|
|
if [ ${SHOW_SETTINGS_FILE} -eq 1 ]; then
|
|
echo "Settings file: ${PROFILE}"
|
|
echo ""; echo ""
|
|
ExitClean
|
|
fi
|
|
|
|
# Initialize and check profile file, auditor name, log file and report file
|
|
if [ ! -r ${PROFILE} ]; then echo "Fatal error: Can't open profile file (${PROFILE})"; exit 1; fi
|
|
if [ "${AUDITORNAME}" = "" ]; then AUDITORNAME="[Unknown]"; fi
|
|
if [ "${LOGFILE}" = "" ]; then LOGFILE="/var/log/lynis.log"; fi
|
|
if [ "${REPORTFILE}" = "" ]; then REPORTFILE="/var/log/lynis-report.dat"; fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# PID :: Check PID file, to avoid multiple instances running at the same time.
|
|
#
|
|
#################################################################################
|
|
#
|
|
|
|
# Decide where to write our PID file. For unprivileged users this will be in their home directory, or /tmp if their
|
|
# home directory isn't set. For root it will be /var/run, or the current workign directory if /var/run doesn't exist.
|
|
MYHOMEDIR=`echo ~ 2> /dev/null`
|
|
if [ "${MYHOMEDIR}" = "" ]; then MYHOMEDIR="/tmp"; fi
|
|
|
|
if [ ${PRIVILEGED} -eq 0 ]; then
|
|
PIDFILE="${MYHOMEDIR}/lynis.pid"
|
|
elif [ -d /var/run ]; then
|
|
PIDFILE="/var/run/lynis.pid"
|
|
else
|
|
PIDFILE="./lynis.pid"
|
|
fi
|
|
|
|
# Check if there is already a PID file in any of the locations (incorrect termination of previous instance)
|
|
if [ -f "${MYHOMEDIR}/lynis.pid" -o -f "./lynis.pid" -o -f "/var/run/lynis.pid" ]; then
|
|
echo ""
|
|
echo " ${WARNING}Warning${NORMAL}: ${WHITE}PID file exists, probably another Lynis process is running.${NORMAL}"
|
|
echo " ------------------------------------------------------------------------------"
|
|
echo " If you are unsure another Lynis process is running currently, you are advised "
|
|
echo " to stop current process and check the process list first. If you cancelled"
|
|
echo " (by using CTRL+C) a previous instance, you can ignore this message."
|
|
echo " "
|
|
echo " You are advised to check for temporary files after program completion."
|
|
echo " ------------------------------------------------------------------------------"
|
|
echo ""
|
|
echo " ${YELLOW}Note: ${WHITE}Cancelling the program can leave temporary files behind${NORMAL}"
|
|
echo ""
|
|
wait_for_keypress
|
|
|
|
# Deleting any stale PID files that might exist. Note: Display function does not work yet at this point
|
|
if [ -f "${MYHOMEDIR}/lynis.pid" ]; then rm -f "${MYHOMEDIR}/lynis.pid"; fi
|
|
if [ -f "./lynis.pid" ]; then rm -f "./lynis.pid"; fi
|
|
if [ -f "/var/run/lynis.pid" ]; then rm -f "/var/run/lynis.pid"; fi
|
|
fi
|
|
|
|
# Ensure symlink attack is not possible, by confirming there is no symlink of the file already
|
|
OURPID=`echo $$`
|
|
if [ -L ${PIDFILE} ]; then
|
|
echo "Found symlinked PID file (${PIDFILE}), quitting"
|
|
ExitFatal
|
|
else
|
|
# Create new PID file writable only by owner
|
|
echo "${OURPID}" > ${PIDFILE}
|
|
chmod 600 ${PIDFILE}
|
|
fi
|
|
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Check program parameters
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Bail out if we didn't get any parameter, or incorrect ones
|
|
if [ ${PARAMCOUNT} -eq 0 -o ${WRONGOPTION} -eq 1 -o ${VIEWHELP} -eq 1 ]; then
|
|
echo ""
|
|
echo " Usage: lynis ${WHITE}[options] ${CYAN}mode${NORMAL}"
|
|
echo ""
|
|
echo ""
|
|
echo " ${CYAN}Mode:${NORMAL}"
|
|
echo ""
|
|
echo " ${GREEN}audit${NORMAL}"
|
|
echo " audit system : Perform security scan"
|
|
echo " audit dockerfile <file> : Analyze Dockerfile"
|
|
echo ""
|
|
echo " ${GREEN}update${NORMAL}"
|
|
echo " update info : Show update details"
|
|
echo " update release : Update Lynis release"
|
|
echo ""
|
|
echo ""
|
|
echo " ${WHITE}Scan options:${NORMAL}"
|
|
echo " --auditor \"<name>\" : Auditor name"
|
|
echo " --dump-options : See all available options"
|
|
echo " --no-log : Don't create a log file"
|
|
echo " --pentest : Non-privileged scan (useful for pentest)"
|
|
echo " --profile <profile> : Scan the system with the given profile file"
|
|
echo " --quick (-Q) : Quick mode, don't wait for user input"
|
|
echo " --tests \"<tests>\" : Run only tests defined by <tests>"
|
|
echo " --tests-category \"<category>\" : Run only tests defined by <category>"
|
|
echo ""
|
|
echo " ${WHITE}Layout options:${NORMAL}"
|
|
echo " --no-colors : Don't use colors in output"
|
|
echo " --quiet (-q) : No output, except warnings"
|
|
echo " --reverse-colors : Optimize color display for light backgrounds"
|
|
echo ""
|
|
echo " ${WHITE}Misc options:${NORMAL}"
|
|
echo " --debug : Debug logging to screen"
|
|
echo " --view-manpage (--man) : View man page"
|
|
echo " --version (-V) : Display version number and quit"
|
|
echo ""
|
|
echo " ${WHITE}Enterprise options:${NORMAL}"
|
|
echo " --plugin-dir \"<path>\" : Define path of available plugins"
|
|
echo " --upload : Upload data to central node"
|
|
echo ""
|
|
echo ""
|
|
|
|
if [ ${WRONGOPTION} -eq 1 ]; then
|
|
echo " ${RED}Error${NORMAL}: ${WHITE}Invalid option '${WRONGOPTION_value}'${NORMAL}"
|
|
else
|
|
if [ ${VIEWHELP} -eq 0 ]; then
|
|
echo " ${RED}Error${NORMAL}: ${WHITE}No scanning mode specified!${NORMAL}"
|
|
fi
|
|
fi
|
|
echo " More scan options are available. See man page and online documentation for details."
|
|
echo ""
|
|
echo "Exiting.."
|
|
# Cleanup PID file if we drop out earlier
|
|
RemovePIDFile
|
|
# Exit with exit code 1
|
|
exit 64
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
if [ ${PRIVILEGED} -eq 0 ]; then
|
|
echo "${WHITE}"
|
|
echo " ###################################################################"
|
|
echo " # #"
|
|
echo " # ${PURPLE}NON-PRIVILEGED SCAN MODE${WHITE} #"
|
|
echo " # #"
|
|
echo " ###################################################################"
|
|
echo "${NORMAL}"
|
|
echo " ${YELLOW}NOTES:${NORMAL}"
|
|
echo " --------------"
|
|
echo " ${WHITE}*${NORMAL} Some tests will be skipped (as they require root permissions)"
|
|
echo " ${WHITE}*${NORMAL} Some tests might fail silently or give different results"
|
|
echo ""
|
|
if [ "${LOGFILE}" = "" -o "${LOGFILE}" = "/dev/null" ]; then
|
|
echo " ${RED}WARNING:${NORMAL}"
|
|
echo " ${WHITE}*${NORMAL} No suggestions or warnings will be displayed in report (due to missing log file)"
|
|
echo ""
|
|
fi
|
|
echo ""
|
|
echo " ${WHITE}Press [ENTER] to continue or [CTRL] + C to break${NORMAL}"
|
|
echo ""
|
|
echo " ###################################################################"
|
|
echo "${NORMAL}"; echo ""
|
|
if [ ${QUICKMODE} -eq 0 ]; then read void; fi
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# OS Detection
|
|
#
|
|
#################################################################################
|
|
#
|
|
SafePerms ${INCLUDEDIR}/osdetection
|
|
. ${INCLUDEDIR}/osdetection
|
|
Display --indent 2 --text "- Detecting OS... " --result DONE --color GREEN
|
|
|
|
# Check hostname
|
|
case ${OS} in
|
|
HP-UX)
|
|
HOSTNAME=`hostname` ;;
|
|
Solaris)
|
|
HOSTNAME=`uname -n` ;;
|
|
*)
|
|
HOSTNAME=`hostname -s 2> /dev/null` ;;
|
|
esac
|
|
FQDN=`hostname 2> /dev/null`
|
|
if [ "${OS}" = "Linux" -a "${HOSTNAME}" = "${FQDN}" ]; then
|
|
FQDN=`hostname -f 2> /dev/null`
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Clear log and report files
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Clear log file and test if it's writable
|
|
logtext "### Starting ${PROGRAM_name} ${PROGRAM_version} with PID ${OURPID}, build date ${PROGRAM_releasedate} ###" > ${LOGFILE}
|
|
if [ $? -gt 0 ]; then
|
|
Display --indent 2 --text "- Clearing log file (${LOGFILE})... " --result WARNING --color RED
|
|
echo "${WARNING}Fatal error${NORMAL}: problem while writing to log file. Check location and permissions."
|
|
RemovePIDFile
|
|
exit 1
|
|
fi
|
|
logtextbreak
|
|
logtext "### ${PROGRAM_copyright} ###"
|
|
|
|
# Clear report file (to avoid appending to an existing file)
|
|
echo "# ${PROGRAM_name} Report" > ${REPORTFILE}
|
|
report "report_version_major=${REPORT_version_major}"
|
|
report "report_version_minor=${REPORT_version_minor}"
|
|
CDATE=`date "+%F %H:%M:%S"`
|
|
report "report_datetime_start=${CDATE}"
|
|
report "auditor=${AUDITORNAME}"
|
|
report "lynis_version=${PROGRAM_version}"
|
|
report "os=${OS}"
|
|
report "os_name=${OS_NAME}"
|
|
report "os_fullname=${OS_FULLNAME}"
|
|
report "os_version=${OS_VERSION}"
|
|
if [ "${OS}" = "Linux" ]; then report "linux_version=${LINUX_VERSION}"; fi
|
|
report "hostname=${HOSTNAME}"
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Show program information to display
|
|
#
|
|
#################################################################################
|
|
#
|
|
if [ ${QUIET} -eq 0 -a ${SHOW_PROGRAM_DETAILS} -eq 1 ]; then
|
|
echo ""
|
|
echo " ---------------------------------------------------"
|
|
echo " Program version: ${PROGRAM_version}"
|
|
echo " Operating system: ${OS}"
|
|
echo " Operating system name: ${OS_NAME}"
|
|
echo " Operating system version: ${OS_VERSION}"
|
|
if [ ! "${OS_MODE}" = "" ]; then echo " Operating system mode: ${OS_MODE}"; fi
|
|
echo " Kernel version: ${OS_KERNELVERSION}"
|
|
echo " Hardware platform: ${HARDWARE}"
|
|
echo " Hostname: ${HOSTNAME}"
|
|
echo " Auditor: ${AUDITORNAME}"
|
|
echo " Profile: ${PROFILE}"
|
|
echo " Log file: ${LOGFILE}"
|
|
echo " Report file: ${REPORTFILE}"
|
|
echo " Report version: ${REPORT_version}"
|
|
echo " Plugin directory: ${PLUGINDIR}"
|
|
echo " ---------------------------------------------------"
|
|
fi
|
|
|
|
logtext "Program version: ${PROGRAM_version}"
|
|
logtext "Operating system: ${OS}"
|
|
logtext "Operating system name: ${OS_NAME}"
|
|
logtext "Operating system version: ${OS_VERSION}"
|
|
if [ ! "${OS_MODE}" = "" ]; then logtext "Operating system mode: ${OS_MODE}"; fi
|
|
logtext "Kernel version: ${OS_KERNELVERSION}"
|
|
if [ ! "${OS_KERNELVERSION_FULL}" = "" ]; then
|
|
logtext "Kernel version (full): ${OS_KERNELVERSION_FULL}"
|
|
fi
|
|
logtext "Hardware platform: ${HARDWARE}"
|
|
logtext "-----------------------------------------------------"
|
|
logtext "Hostname: ${HOSTNAME}"
|
|
logtext "Auditor: ${AUDITORNAME}"
|
|
logtext "Profile: ${PROFILE}"
|
|
logtext "Include directory: ${INCLUDEDIR}"
|
|
logtext "Plugin directory: ${PLUGINDIR}"
|
|
logtext "-----------------------------------------------------"
|
|
logtext "Log file: ${LOGFILE}"
|
|
logtext "Report file: ${REPORTFILE}"
|
|
logtext "Report version: ${REPORT_version}"
|
|
logtext "-----------------------------------------------------"
|
|
logtext "BusyBox used: ${SHELL_IS_BUSYBOX}"
|
|
|
|
logtextbreak
|
|
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Read profile/template/plugins
|
|
#
|
|
#################################################################################
|
|
#
|
|
SafePerms ${INCLUDEDIR}/profiles
|
|
. ${INCLUDEDIR}/profiles
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Check for program update (people tend to be lazy and don't perform updates =))
|
|
#
|
|
#################################################################################
|
|
#
|
|
logtext "Test: Checking for program update..."
|
|
UPDATE_AVAILABLE=0
|
|
if [ ${SKIP_UPGRADE_TEST} -eq 1 ]; then
|
|
logtext "Upgrade test skipped due profile option set (skip_upgrade_test)"
|
|
PROGRAM_LV="${PROGRAM_AC}"
|
|
else
|
|
CheckUpdates
|
|
fi
|
|
if [ "${PROGRAM_AC}" = "" -o "${PROGRAM_LV}" = "" ]; then
|
|
Display --indent 2 --text "- Program update status... " --result UNKNOWN --color YELLOW
|
|
logtext "Result: Update check failed. No network connection?"
|
|
logtext "Info: to perform an automatic update check, outbound DNS connections should be allowed (TXT record)."
|
|
# Set both to safe values
|
|
PROGRAM_AC=0; PROGRAM_LV=0
|
|
else
|
|
logtext "Current installed version : ${PROGRAM_AC}"
|
|
logtext "Latest stable version : ${PROGRAM_LV}"
|
|
if [ ${PROGRAM_LV} -gt ${PROGRAM_AC} ]; then
|
|
# Check if current version is REALLY outdated (10 versions ago)
|
|
PROGRAM_MINVERSION=`expr ${PROGRAM_LV} - 10`
|
|
logtext "Minimum required version : ${PROGRAM_MINVERSION}"
|
|
if [ ${PROGRAM_MINVERSION} -gt ${PROGRAM_AC} ]; then
|
|
Display --indent 2 --text "- Program update status... " --result "WARNING" --color RED
|
|
logtext "Result: This version is VERY outdated. Newer ${PROGRAM_name} release available!"
|
|
ReportWarning "NONE" "Version of Lynis is very old and should be updated"
|
|
report "lynis_update_available=1"
|
|
UPDATE_AVAILABLE=1
|
|
else
|
|
Display --indent 2 --text "- Program update status... " --result "UPDATE AVAILABLE" --color YELLOW
|
|
logtext "Result: newer ${PROGRAM_name} release available!"
|
|
ReportSuggestion "NONE" "Version of Lynis outdated, consider upgrading to the latest version"
|
|
report "lynis_update_available=1"
|
|
UPDATE_AVAILABLE=1
|
|
fi
|
|
echo ""
|
|
echo " ==============================================================================="
|
|
echo " ${NOTICE}${PROGRAM_name} update available${NORMAL}"
|
|
echo " ==============================================================================="
|
|
echo ""
|
|
echo " Current version : ${YELLOW}${PROGRAM_AC}${NORMAL} Latest version : ${GREEN}${PROGRAM_LV}${NORMAL}"
|
|
echo ""
|
|
echo " ${WHITE}Please update to the latest version for new features, bug fixes, tests"
|
|
echo " and baselines.${NORMAL}"
|
|
echo ""
|
|
echo " https://cisofy.com/downloads/"
|
|
echo ""
|
|
echo " ==============================================================================="
|
|
echo ""
|
|
sleep 5
|
|
#wait_for_keypress
|
|
else
|
|
if [ ${UPDATE_CHECK_SKIPPED} -eq 0 ]; then
|
|
Display --indent 2 --text "- Program update status... " --result "NO UPDATE" --color GREEN
|
|
logtext "No ${PROGRAM_name} update available."
|
|
report "lynis_update_available=0"
|
|
else
|
|
Display --indent 2 --text "- Program update status... " --result "SKIPPED" --color YELLOW
|
|
logtext "Update check skipped due to constraints (e.g. missing dig binary)"
|
|
report "lynis_update_available=-1"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
logtextbreak
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Check which binaries are available to the scanning process
|
|
if [ -f ${INCLUDEDIR}/binaries ]; then
|
|
SafePerms ${INCLUDEDIR}/binaries
|
|
. ${INCLUDEDIR}/binaries
|
|
fi
|
|
logtextbreak
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Check if this is a virtual machine (after the appropriate binaries are found)
|
|
IsVirtualMachine
|
|
#
|
|
#################################################################################
|
|
#
|
|
|
|
if [ ${RUN_PLUGINS} -eq 1 ]; then
|
|
|
|
N_PLUGIN=0
|
|
N_PLUGIN_ENABLED=0
|
|
|
|
# Plugins function
|
|
RunPlugins()
|
|
{
|
|
if [ $# -eq 0 ]; then echo "RunPlugins should be started with phase number"; ExitFatal; fi
|
|
PLUGIN_PHASE=$1
|
|
if [ ${PLUGIN_PHASE} -eq 0 -o ${PLUGIN_PHASE} -gt 2 ]; then echo "Incorrect phase number when calling RunPlugins"; ExitFatal; fi
|
|
logtextbreak
|
|
InsertPluginSection "Plugins (phase ${PLUGIN_PHASE})"
|
|
if [ ${PLUGIN_PHASE} -eq 1 ]; then
|
|
Display --text "Note: plugins have more extensive tests, which may take a few minutes to complete"
|
|
Display --text " "
|
|
logtext "Searching plugins..."
|
|
fi
|
|
|
|
# Search plugins
|
|
FIND_PLUGINS=`find ${PLUGINDIR} -type f -name "plugin_[a-z]*" -exec echo \{\} \; | sort`
|
|
for PLUGIN_FILE in ${FIND_PLUGINS}; do
|
|
logtext "Found plugin file: ${PLUGIN_FILE}"
|
|
# Double check if output is a valid file name
|
|
if [ -f ${PLUGIN_FILE} ]; then
|
|
FIND2=`grep "^# PLUGIN_NAME=" ${PLUGIN_FILE} | awk -F= '{ print $2 }'`
|
|
if [ ! "${FIND2}" = "" -a ! "${FIND2}" = "[plugin_name]" ]; then
|
|
if [ ${PLUGIN_PHASE} -eq 1 ]; then N_PLUGIN=`expr ${N_PLUGIN} + 1`; fi
|
|
FIND3=`grep "^plugin=${FIND2}" ${PROFILE}`
|
|
if [ ! "${FIND3}" = "" ]; then
|
|
logtext "Plugin ${FIND2} is enabled"
|
|
# Plugins should have at least a _phase1 part, _phase2 is optional at this moment
|
|
PLUGINFILE="${PLUGINDIR}/plugin_${FIND2}_phase${PLUGIN_PHASE}"
|
|
if [ -f ${PLUGINFILE} ]; then
|
|
PLUGIN_VERSION=`grep "^# PLUGIN_VERSION=" ${PLUGIN_FILE} | awk -F= '{ print $2 }'`
|
|
PLUGIN_VERSION_NODOTS=`echo ${PLUGIN_VERSION} | sed 's/.//g'`
|
|
FIND4=`ls -l ${PLUGINFILE} | cut -c 2-10`
|
|
if [ "${FIND4}" = "rw-r--r--" -o "${FIND4}" = "rw-r-----" -o "${FIND4}" = "rw-------" -o "${FIND4}" = "r--------" ]; then
|
|
logtext "Including plugin file: ${PLUGINFILE} (version: ${PLUGIN_VERSION})"
|
|
report "plugin_enabled_phase${PLUGIN_PHASE}[]=${FIND2}|${PLUGIN_VERSION}|"
|
|
if [ ${PLUGIN_PHASE} -eq 1 ]; then N_PLUGIN_ENABLED=`expr ${N_PLUGIN_ENABLED} + 1`; fi
|
|
Display --indent 2 --text "- ${CYAN}Plugin${NORMAL}: ${WHITE}${FIND2}${NORMAL}"
|
|
if [ ${PLUGIN_PHASE} -eq 1 ]; then Progress " ["; fi
|
|
. ${PLUGINFILE}
|
|
if [ ${PLUGIN_PHASE} -eq 1 ]; then Progress "]"; Progress --finish; fi
|
|
logtextbreak
|
|
logtext "Result: ${FIND2} plugin (phase ${PLUGIN_PHASE}) finished"
|
|
else
|
|
logtext "Plugin ${FIND2}: Skipped (bad file permissions, should be 640, 600 or 400)"
|
|
fi
|
|
else
|
|
logtext "Plugin ${FIND2}: Skipped (can't find file ${PLUGINFILE})"
|
|
fi
|
|
else
|
|
logtext "Plugin ${FIND2}: Skipped (not enabled)"
|
|
fi
|
|
else
|
|
logtext "Skipping plugin file ${PLUGIN_FILE} (no valid plugin name found)"
|
|
fi
|
|
fi
|
|
logtext "--"
|
|
done
|
|
logtext "Result: Found ${N_PLUGIN} plugins of which ${N_PLUGIN_ENABLED} are enabled"
|
|
logtext "Result: Plugins ${PLUGIN_PHASE} finished"
|
|
}
|
|
RunPlugins 1
|
|
|
|
if [ ${N_PLUGIN_ENABLED} -eq 0 ]; then
|
|
Display --indent 2 --text "- Plugins enabled " --result "NONE" --color WHITE
|
|
report "plugins_enabled=0"
|
|
else
|
|
report "plugins_enabled=1"
|
|
fi
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Get host ID
|
|
logtextbreak
|
|
GetHostID
|
|
# Check if result is not empty (no blank, or hash of blank value, or minus, or zeros)
|
|
if [ ! "${HOSTID}" = "-" -a ! "${HOSTID}" = "" -a ! "${HOSTID}" = "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc" -a ! "${HOSTID}" = "6ef1338f520d075957424741d7ed35ab5966ae97" ]; then
|
|
logtext "Info: found valid HostID ${HOSTID}"
|
|
report "hostid=${HOSTID}"
|
|
else
|
|
logtext "Info: no HostID found or invalid one"
|
|
fi
|
|
if [ ! "${MACHINEID}" = "" ]; then
|
|
logtext "Info: found a machine ID ${MACHINEID}"
|
|
report "machineid=${MACHINEID}"
|
|
else
|
|
logtext "Info: no machine ID found"
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
|
|
if [ ${RUN_TESTS} -eq 1 ]; then
|
|
|
|
logtextbreak
|
|
# Test sections
|
|
if [ "${TESTS_CATEGORY_TO_PERFORM}" = "" ]; then
|
|
logtext "Info: perform tests from all categories"
|
|
|
|
INCLUDE_TESTS="boot_services kernel memory_processes authentication shells \
|
|
filesystems storage storage_nfs nameservices ports_packages networking printers_spools \
|
|
mail_messaging firewalls webservers ssh snmp databases ldap php squid logging \
|
|
insecure_services banners scheduling accounting time crypto virtualization containers \
|
|
mac_frameworks file_integrity tooling malware file_permissions homedirs \
|
|
kernel_hardening hardening"
|
|
else
|
|
INCLUDE_TESTS="${TESTS_CATEGORY_TO_PERFORM}"
|
|
logtext "Info: only performing tests from categories: ${TESTS_CATEGORY_TO_PERFORM}"
|
|
fi
|
|
|
|
# Include available tests
|
|
for INCLUDE_TEST in ${INCLUDE_TESTS}; do
|
|
|
|
# Test if file exists, then if permissions are correct
|
|
if [ -f ${INCLUDEDIR}/tests_${INCLUDE_TEST} ]; then
|
|
FIND=`ls -l ${INCLUDEDIR}/tests_${INCLUDE_TEST} | cut -c 2-10`
|
|
if [ "${FIND}" = "rw-r--r--" -o "${FIND}" = "rw-r-----" -o "${FIND}" = "rw-------" -o "${FIND}" = "r--------" ]; then
|
|
. ${INCLUDEDIR}/tests_${INCLUDE_TEST}
|
|
else
|
|
logtext "Exception: skipping test category ${INCLUDE_TEST}, file ${INCLUDEDIR}/tests_${INCLUDE_TEST} has bad permissions (should be 640, 600 or 400)"
|
|
ReportWarning "NONE" "H" "Invalid permissions on tests file tests_${INCLUDE_TEST}"
|
|
# Insert a section and warn user also on screen
|
|
InsertSection "General"
|
|
Display --indent 2 --text "- Running test category ${INCLUDE_TEST}... " --result "SKIPPED" --color RED
|
|
fi
|
|
else
|
|
echo "Error: Can't find file (category: ${INCLUDE_TEST})"
|
|
fi
|
|
|
|
done
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
|
|
if [ ${RUN_TESTS} -eq 1 ]; then
|
|
|
|
InsertSection "Custom Tests"
|
|
logtext "Test: Checking for tests_custom file"
|
|
# Custom tests
|
|
if [ -f ${INCLUDEDIR}/tests_custom ]; then
|
|
logtext "Result: tests_custom file found in include directory"
|
|
logtext "Test: checking file permissions of tests_custom file"
|
|
FIND=`ls -l ${INCLUDEDIR}/tests_custom | cut -c 2-10`
|
|
if [ "${FIND}" = "rw-r--r--" -o "${FIND}" = "rw-r-----" -o "${FIND}" = "rw-------" -o "${FIND}" = "r--------" ]; then
|
|
Display --indent 2 --text "- Start custom tests... "
|
|
logtext "Result: file permissions fine, running custom tests"
|
|
SafePerms ${INCLUDEDIR}/tests_custom
|
|
. ${INCLUDEDIR}/tests_custom
|
|
else
|
|
logtext "Exception: skipping custom tests, file has bad permissions (should be 640, 600 or 400)"
|
|
ReportWarning "NONE" "H" "Invalid permissions on custom tests file"
|
|
Display --indent 2 --text "- Running custom tests... " --result "WARNING" --color RED
|
|
fi
|
|
else
|
|
Display --indent 2 --text "- Running custom tests... " --result "NONE" --color WHITE
|
|
fi
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Run helpers
|
|
#
|
|
#################################################################################
|
|
#
|
|
if [ ${RUN_HELPERS} -eq 1 ]; then
|
|
if [ ! "${HELPER}" = "" ]; then
|
|
logtext "Helper tool is $HELPER"
|
|
if [ -f ${INCLUDEDIR}/helper_${HELPER} ]; then
|
|
SafePerms ${INCLUDEDIR}/helper_${HELPER}
|
|
logtext "Running helper tool ${HELPER} with params: ${HELPER_PARAMS}"
|
|
InsertPluginSection "Helper: ${HELPER}"
|
|
. ${INCLUDEDIR}/helper_${HELPER} ${HELPER_PARAMS}
|
|
else
|
|
echo "Error, could not find helper"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Run phase 2 of plugins
|
|
#
|
|
#################################################################################
|
|
#
|
|
if [ ${RUN_PLUGINS} -eq 1 ]; then
|
|
RunPlugins 2
|
|
fi
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Show test results overview
|
|
#
|
|
#################################################################################
|
|
#
|
|
# Store total performed tests
|
|
report "lynis_tests_done=${CTESTS_PERFORMED}"
|
|
CDATE=`date "+%F %H:%M:%S"`
|
|
report "report_datetime_end=${CDATE}"
|
|
|
|
# Show report
|
|
if [ -f ${INCLUDEDIR}/report ]; then SafePerms ${INCLUDEDIR}/report; . ${INCLUDEDIR}/report; fi
|
|
|
|
# Show tool tips
|
|
if [ -f ${INCLUDEDIR}/hints_tips ]; then SafePerms ${INCLUDEDIR}/hints_tips; . ${INCLUDEDIR}/hints_tips; fi
|
|
|
|
logtext "================================================================================"
|
|
logtext "Tests performed: ${CTESTS_PERFORMED}"
|
|
logtext "Total tests: ${TOTAL_TESTS}"
|
|
logtext "Active plugins: ${N_PLUGIN_ENABLED}"
|
|
logtext "Total plugins: ${N_PLUGIN}"
|
|
logtext "================================================================================"
|
|
report "tests_executed=${TESTS_EXECUTED}"
|
|
report "tests_skipped=${TESTS_SKIPPED}"
|
|
report "finish=true"
|
|
|
|
# Upload data
|
|
if [ ${UPLOAD_DATA} -eq 1 ]; then
|
|
if [ -f ${INCLUDEDIR}/data_upload ]; then
|
|
SafePerms ${INCLUDEDIR}/data_upload
|
|
. ${INCLUDEDIR}/data_upload
|
|
else
|
|
echo "Fatal error: can't find upload_data script"
|
|
fi
|
|
fi
|
|
|
|
logtext "${PROGRAM_name} ${PROGRAM_version}"
|
|
logtext "${PROGRAM_copyright}"
|
|
logtext "${PROGRAM_extrainfo}"
|
|
logtext "Program ended successfully"
|
|
logtext "================================================================================"
|
|
|
|
# Clean exit (Delete PID file)
|
|
if [ ${TOTAL_WARNINGS} -gt 0 ]; then
|
|
ExitCustom 78
|
|
else
|
|
ExitClean
|
|
fi
|
|
|
|
# The End
|
|
|
|
###########################################################################
|
|
##%HASH-SHA1%----------------------------%
|
|
###########################################################################
|
|
|
|
#
|
|
#================================================================================
|
|
# Lynis - Copyright 2007-2016, Michael Boelen, CISOfy - https://cisofy.com
|