From 62d9a18861f53c593e0aea763177ea78cf5833f7 Mon Sep 17 00:00:00 2001 From: hlein Date: Wed, 8 Mar 2017 09:24:24 -0700 Subject: [PATCH] A bunch of Solaris compatibility tweaks (#367) * Work around Solaris' /bin/sh not being POSIX. If /usr/xpg4/bin/sh is present, we are (definitely?) on Solaris or a derivative, and /bin/sh cannot be trusted to support POSIX, but /usr/xpg4/bin/sh can be. Exec it right away. * Work around Solaris 'which' command oddity. Solaris' (at least) 'which' command outputs not-found errors to STDOUT instead of STDERR. This makes "did we get any output from which" checks insufficient; piping to grep -v the "no foo in ..." message should work. Note that this patch set includes all such uses of which that I could find, including ones that should never be reached on Solaris (i.e. only executed on some other OS) just for consistency. * Improved alternate-sh exec to avoid looping. * Solaris' /usr/ucb/echo supports -n. * Check for the best hash type that openssl supports. When using openssl to generate hashes, do not assume it supports sha256; try that, then sha1, then give up and use md5. * Solaris does not support sed -i; use a tempfile. * Use the full path for modinfo. When running as non-root, /usr/sbin/ might not be in PATH. include/tests_accounting already calls modinfo by full path, but include/tests_kernel did not. * Solaris find does not support -maxdepth. This mirrors the logic already in tests_homedirs. * Use PSBINARY instead of ps. * Work around Solaris' date not supporting +%s. Printing nawk's srand value is a bizarre but apparently once popular workaround for there being no normal userland command to print UNIX epoch seconds. A perl one-liner is the other common approach, but nawk may be more reliably present on Solaris than perl. * Revert to using sha1 for HOSTID. * Whitespace cleanup for openssl hash tests. --- include/functions | 48 ++++++++++++++++++++----------- include/helper_show | 2 +- include/helper_system_remote_scan | 4 +-- include/helper_update | 6 ++-- include/osdetection | 4 +-- include/tests_authentication | 16 +++++++++-- include/tests_kernel | 2 +- include/tests_nameservices | 2 +- include/tests_ports_packages | 4 +-- include/tests_scheduling | 2 +- include/tests_shells | 2 +- include/tests_tooling | 2 +- lynis | 12 +++++++- 13 files changed, 71 insertions(+), 35 deletions(-) diff --git a/include/functions b/include/functions index ba830c69..a9078d43 100644 --- a/include/functions +++ b/include/functions @@ -151,7 +151,11 @@ else Debug "Setting '${SETTING}' was already configured, overwriting previous line '${FIND}' in ${SETTINGS_FILE} with value '${VALUE}'" # Delete line first, then add new value (inline search and replace is messy) - sed -i -e '/^'"${SETTING}"';/d' ${SETTINGS_FILE} + CreateTempFile + TEMP_SETTINGS_FILE="${TEMP_FILE}" + cat ${SETTINGS_FILE} > ${TEMP_SETTINGS_FILE} + sed -e '/^'"${SETTING}"';/d' ${TEMP_SETTINGS_FILE} > ${SETTINGS_FILE} + rm ${TEMP_SETTINGS_FILE} echo "${SETTING};${VALUE};${DESCRIPTION};" >> ${SETTINGS_FILE} fi else @@ -250,16 +254,16 @@ PROGRAM_LV="0000000000"; DB_MALWARE_LV="0000000000"; DB_FILEPERMS_LV="0000000000" if [ ${RUN_UPDATE_CHECK} -eq 1 ]; then LYNIS_LV_RECORD="lynis-latest-version.cisofy.com." - FIND=$(which dig 2> /dev/null) + FIND=$(which dig 2> /dev/null | grep -v "no [^ ]* in") if [ ! -z "${FIND}" ]; then PROGRAM_LV=$(dig +short +time=3 -t txt lynis-latest-version.cisofy.com 2> /dev/null | grep -v "connection timed out" | sed 's/[".]//g' | grep "^[1-9][0-9][0-9]$") else - FIND=$(which host 2> /dev/null) + FIND=$(which host 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${FIND}" ]; then PROGRAM_LV=$(host -t txt -W 3 lynis-latest-version.cisofy.com 2> /dev/null | grep -v "connection timed out" | awk '{ if ($1=="lynis-latest-version.cisofy.com" && $3=="text") { print $4 }}' | sed 's/"//g' | grep "^[1-9][0-9][0-9]$") if [ "${PROGRAM_LV}" = "" ]; then PROGRAM_LV=0; fi else - FIND=$(which drill 2> /dev/null) + FIND=$(which drill 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${FIND}" ]; then PROGRAM_LV=$(drill txt ${LYNIS_LV_RECORD} | awk '{ if ($1=="lynis-latest-version.cisofy.com." && $4=="TXT") { print $5 }}' | tr -d '"' | grep "^[1-9][0-9][0-9]$") if [ -z "${PROGRAM_LV}" ]; then PROGRAM_LV=0; fi @@ -778,14 +782,24 @@ # Avoid some hashes (empty, only zeros) BLACKLISTED_HASHES="6ef1338f520d075957424741d7ed35ab5966ae97 adc83b19e793491b1c6ea0fd8b46cd9f32e592fc" # Check which utilities we can use (e.g. lynis show hostids). Normally these are detected during binaries collecting. - if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1sum 2> /dev/null); fi - if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1 2> /dev/null); fi - if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256sum 2> /dev/null); fi - if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256 2> /dev/null); fi - if [ "${CSUMBINARY}" = "" ]; then CSUMBINARY=$(which csum 2> /dev/null); fi - if [ "${OPENSSLBINARY}" = "" ]; then OPENSSLBINARY=$(which openssl 2> /dev/null); fi - if [ "${IFCONFIGBINARY}" = "" ]; then IFCONFIGBINARY=$(which ifconfig 2> /dev/null); fi - if [ "${IPBINARY}" = "" ]; then IPBINARY=$(which ip 2> /dev/null); fi + if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1sum 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${SHA1SUMBINARY}" = "" ]; then SHA1SUMBINARY=$(which sha1 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256sum 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${SHA256SUMBINARY}" = "" ]; then SHA256SUMBINARY=$(which sha256 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${CSUMBINARY}" = "" ]; then CSUMBINARY=$(which csum 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${OPENSSLBINARY}" = "" ]; then OPENSSLBINARY=$(which openssl 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${IFCONFIGBINARY}" = "" ]; then IFCONFIGBINARY=$(which ifconfig 2> /dev/null | grep -v "no [^ ]* in "); fi + if [ "${IPBINARY}" = "" ]; then IPBINARY=$(which ip 2> /dev/null | grep -v "no [^ ]* in "); fi + + # If using openssl, use the best hash type it supports + if [ ! "${OPENSSLBINARY}" = "" ]; then + OPENSSL_HASHLIST=$(openssl dgst -h 2>&1) + for OPENSSL_HASHTYPE in sha256 sha1 md5 ; do + if echo "${OPENSSL_HASHLIST}" | grep "^-${OPENSSL_HASHTYPE} " >/dev/null ; then + break + fi + done + fi if [ ! "${SHA1SUMBINARY}" = "" -o ! "${OPENSSLBINARY}" = "" -o ! "${CSUMBINARY}" = "" ]; then @@ -1033,8 +1047,8 @@ HASH2=$(echo ${STRING_TO_HASH} | ${SHA256SUMBINARY} | awk '{ print $1 }') HASH_HOSTNAME=$(echo ${HOSTNAME} | ${SHA256SUMBINARY} | awk '{ print $1 }') elif [ ! "${OPENSSLBINARY}" = "" ]; then - HASH2=$(echo ${STRING_TO_HASH} | ${OPENSSLBINARY} sha -sha256 | awk '{ print $2 }') - HASH_HOSTNAME=$(echo ${HOSTNAME} | ${OPENSSLBINARY} sha -sha256 | awk '{ print $2 }') + HASH2=$(echo ${STRING_TO_HASH} | ${OPENSSLBINARY} dgst -${OPENSSL_HASHTYPE} | awk '{ print $2 }') + HASH_HOSTNAME=$(echo ${HOSTNAME} | ${OPENSSLBINARY} dgst -${OPENSSL_HASHTYPE} | awk '{ print $2 }') fi LogText "Hash (hostname): ${HASH_HOSTNAME}" LogText "Hash (ssh or machineid): ${HASH2}" @@ -1178,7 +1192,7 @@ ################################################################################ IsNotebook() { - FIND=$(which laptop-detect 2> /dev/null) + FIND=$(which laptop-detect 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${FIND}" ]; then Debug "Testing if we are a notebook" laptop-detect @@ -2547,7 +2561,7 @@ if [ "${OS}" = "macOS" ]; then # If a Python binary is found, use the one in path if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${PYTHONBINARY}" = "" ]; then - FIND=$(which python 2> /dev/null) + FIND=$(which python 2> /dev/null | grep -v "no [^ ]* in ") if [ ! "${FIND}" = "" ]; then LogText "Setting temporary pythonbinary variable"; PYTHONBINARY="${FIND}"; fi fi @@ -2558,7 +2572,7 @@ fi else if [ ${BINARY_SCAN_FINISHED} -eq 0 -a "${READLINKBINARY}" = "" ]; then - FIND=$(which readlink 2> /dev/null) + FIND=$(which readlink 2> /dev/null | grep -v "no [^ ]* in ") if [ ! "${FIND}" = "" ]; then LogText "Setting temporary readlinkbinary variable"; READLINKBINARY="${FIND}"; fi fi diff --git a/include/helper_show b/include/helper_show index b543ba3b..c5f20ddd 100644 --- a/include/helper_show +++ b/include/helper_show @@ -138,7 +138,7 @@ if [ $# -gt 0 ]; then CHANGELOG="${FILEPATH}/CHANGELOG.md" # Check also for gzipped changelog elif [ -f ${FILEPATH}/changelog.gz ]; then - ZCAT=$(which zcat 2> /dev/null) + ZCAT=$(which zcat 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${ZCAT}" ]; then CreateTempFile CHANGELOG="${TEMP_FILE}" diff --git a/include/helper_system_remote_scan b/include/helper_system_remote_scan index 58d3e121..9f347bf8 100644 --- a/include/helper_system_remote_scan +++ b/include/helper_system_remote_scan @@ -49,8 +49,8 @@ # Enable screen output again QUIET=0 - SCP_BINARY=$(which scp 2> /dev/null) - SSH_BINARY=$(which ssh 2> /dev/null) + SCP_BINARY=$(which scp 2> /dev/null | grep -v "no [^ ]* in ") + SSH_BINARY=$(which ssh 2> /dev/null | grep -v "no [^ ]* in ") if [ "${SCP_BINARY}" = "" ]; then echo "Could not find scp binary"; ExitFatal; fi if [ "${SSH_BINARY}" = "" ]; then echo "Could not find ssh binary"; ExitFatal; fi diff --git a/include/helper_update b/include/helper_update index 44f939a6..4b8bced4 100644 --- a/include/helper_update +++ b/include/helper_update @@ -38,9 +38,9 @@ SERVER_VERSION="" PERFORM_UPGRADE=0 QUIET=0 -WGET_EXISTS=$(which wget 2> /dev/null) -CURL_EXISTS=$(which curl 2> /dev/null) -FETCH_EXISTS=$(which fetch 2> /dev/null) +WGET_EXISTS=$(which wget 2> /dev/null | grep -v "no [^ ]* in ") +CURL_EXISTS=$(which curl 2> /dev/null | grep -v "no [^ ]* in ") +FETCH_EXISTS=$(which fetch 2> /dev/null | grep -v "no [^ ]* in ") # Update version if [ "$1" = "release" ]; then diff --git a/include/osdetection b/include/osdetection index aad5cf18..117f045f 100644 --- a/include/osdetection +++ b/include/osdetection @@ -412,7 +412,7 @@ OS_FULLNAME=$(cat /etc/vmware-release) OS_VERSION=$(uname -r) fi - HAS_VMWARE_UTIL=$(which vmware 2> /dev/null) + HAS_VMWARE_UTIL=$(which vmware 2> /dev/null | grep -v "no [^ ]* in ") if [ ! "${HAS_VMWARE_UTIL}" = "" ]; then IS_VMWARE_ESXI=$(vmware -vl | grep VMware ESXi) if [ ! "${IS_VMWARE_ESXI}" = "" ]; then @@ -440,7 +440,7 @@ "AIX") ECHOCMD="echo" ;; "DragonFly"|"FreeBSD"|"NetBSD") ECHOCMD="echo -e"; ECHONB="echo -n" ;; "macOS" | "Mac OS X") ECHOCMD="echo"; ECHONB="/bin/echo -n" ;; - "Solaris") ECHOCMD="echo" ;; + "Solaris") ECHOCMD="echo" ; test -f /usr/ucb/echo && ECHONB="/usr/ucb/echo -n" ;; "Linux") # Check if dash is used (Debian/Ubuntu) DEFAULT_SHELL=$(ls -l /bin/sh | awk -F'>' '{print $2}') diff --git a/include/tests_authentication b/include/tests_authentication index 9ab7bf43..51e58130 100644 --- a/include/tests_authentication +++ b/include/tests_authentication @@ -650,7 +650,12 @@ if [ -d ${DIR} -a ! -L ${DIR} ]; then LogText "Result: directory ${DIR} exists" # Search in the specified directory - FIND=$(find ${DIR} -maxdepth 1 -type f -name "pam_*.so" -print | sort) + if [ "${OS}" = "Solaris" ]; then + # Solaris doesn't support -maxdepth + FIND=$(find ${DIR} -type f -name "pam_*.so" -print | sort) + else + FIND=$(find ${DIR} -maxdepth 1 -type f -name "pam_*.so" -print | sort) + fi if [ ! "${FIND}" = "" ]; then FOUND=1; fi for FILE in ${FIND}; do LogText "Found file: ${FILE}" @@ -809,7 +814,14 @@ Register --test-no AUTH-9288 --preqs-met ${PREQS_MET} --weight L --network NO --root-only YES --category security --description "Checking for expired passwords" if [ ${SKIPTEST} -eq 0 ]; then if FileIsReadable /etc/shadow; then - DAYS_SINCE_EPOCH=$(($(date --utc +%s)/86400)) + + if [ "$OS" = "Solaris" ]; then + NOW=$(nawk 'BEGIN{print srand()}') + else + NOW=$(date "+%s") + fi + + DAYS_SINCE_EPOCH=$((${NOW}/86400)) LogText "Data: Days since epoch is ${DAYS_SINCE_EPOCH}" LogText "Test: collecting accounts which have an expired password (last day changed + maximum change time)" # Skip fields with a !, *, or x, or !* (field $3 is last changed, $5 is maximum changed) diff --git a/include/tests_kernel b/include/tests_kernel index feead1a0..5367e1d5 100644 --- a/include/tests_kernel +++ b/include/tests_kernel @@ -350,7 +350,7 @@ Register --test-no KRNL-5770 --os Solaris --weight L --network NO --category security --description "Checking active kernel modules" if [ ${SKIPTEST} -eq 0 ]; then LogText "Test: searching loaded kernel modules" - FIND=$(modinfo -c -w | ${GREPBINARY} -v "UNLOADED" | ${GREPBINARY} LOADED | ${AWKBINARY} '{ print $3 }' | sort) + FIND=$(/usr/sbin/modinfo -c -w | ${GREPBINARY} -v "UNLOADED" | ${GREPBINARY} LOADED | ${AWKBINARY} '{ print $3 }' | sort) if [ ! "${FIND}" = "" ]; then for I in ${FIND}; do LogText "Found module: ${I}" diff --git a/include/tests_nameservices b/include/tests_nameservices index a1f3168d..cce4d07e 100644 --- a/include/tests_nameservices +++ b/include/tests_nameservices @@ -267,7 +267,7 @@ if [ ${UNBOUND_RUNNING} -eq 1 ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi Register --test-no NAME-4036 --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Check Unbound configuration file" if [ ${SKIPTEST} -eq 0 ]; then - FIND=$(which unbound-checkconf) + FIND=$(which unbound-checkconf | grep -v "no [^ ]* in ") if [ ! "${FIND}" = "" ]; then LogText "Test: running unbound-checkconf" # Don't capture any output, just gather exit code (0 is fine, otherwise bad) diff --git a/include/tests_ports_packages b/include/tests_ports_packages index c1727a46..f6681df4 100644 --- a/include/tests_ports_packages +++ b/include/tests_ports_packages @@ -87,7 +87,7 @@ # # Test : PKGS-7303 # Description : Query brew package manager - FIND=$(which brew 2> /dev/null) + FIND=$(which brew 2> /dev/null | grep -v "no [^ ]* in ") if [ ! "${FIND}" = "" ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi Register --test-no PKGS-7303 --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Query brew package manager" if [ ${SKIPTEST} -eq 0 ]; then @@ -223,7 +223,7 @@ Register --test-no PKGS-7312 --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Checking available updates for pacman based system" if [ ${SKIPTEST} -eq 0 ]; then FOUND=0 - FIND=$(which checkupdates 2> /dev/null) + FIND=$(which checkupdates 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${FIND}" ]; then FIND=$(checkupdates) for I in ${FIND}; do diff --git a/include/tests_scheduling b/include/tests_scheduling index 0ac7268d..43c9fad7 100644 --- a/include/tests_scheduling +++ b/include/tests_scheduling @@ -35,7 +35,7 @@ # Description : Check cron daemon Register --test-no SCHD-7702 --weight L --network NO --category security --description "Check status of cron daemon" if [ ${SKIPTEST} -eq 0 ]; then - FIND=$(ps aux | ${EGREPBINARY} "( cron$|/cron(d)? )") + FIND=$(${PSBINARY} aux | ${EGREPBINARY} "( cron$|/cron(d)? )") if [ "${FIND}" = "" ]; then LogText "Result: no cron daemon found" else diff --git a/include/tests_shells b/include/tests_shells index e6f13eee..6fb612a1 100644 --- a/include/tests_shells +++ b/include/tests_shells @@ -283,7 +283,7 @@ FIND=$(${EGREPBINARY} '(/usr)?(/local)?/bin/bash' /etc/shells | ${GREPBINARY} -v "^#" | head -1) else LogText "Test: checking if bash is available via which command" - FIND=$(which bash 2> /dev/null | head -1) + FIND=$(which bash 2> /dev/null | grep -v "no [^ ]* in " | head -1) fi LogText "Result: command revealed ${FIND} as output" diff --git a/include/tests_tooling b/include/tests_tooling index c4ed6123..e26a23aa 100644 --- a/include/tests_tooling +++ b/include/tests_tooling @@ -183,7 +183,7 @@ # Continue if tooling is available and configuration file found if [ ${FAIL2BAN_FOUND} -eq 1 -a ! "${FAIL2BAN_CONFIG}" = "" ]; then Report "fail2ban_config=${FAIL2BAN_CONFIG}" - FAIL2BANCLIENT=$(which fail2ban-client 2> /dev/null) + FAIL2BANCLIENT=$(which fail2ban-client 2> /dev/null | grep -v "no [^ ]* in ") if [ ! -z "${FAIL2BANCLIENT}" ]; then PERFORM_FAIL2BAN_TESTS=1; fi fi fi diff --git a/lynis b/lynis index f4250bc9..33b67165 100755 --- a/lynis +++ b/lynis @@ -1,5 +1,10 @@ #!/bin/sh +# In Solaris /bin/sh is not POSIX, but /usr/xpg4/bin/sh is. +# Switch to /usr/xpg4/bin/sh if it exists and we are not already running it. +test "$_" != "/usr/xpg4/bin/sh" && test -f /usr/xpg4/bin/sh && \ +exec /usr/xpg4/bin/sh "$0" "$@" + ################################################################################# # # Lynis @@ -698,7 +703,12 @@ ${NORMAL} fi # Test for older releases, without testing via update mechanism - NOW=$(date "+%s") + if [ "$OS" = "Solaris" ]; then + NOW=$(nawk 'BEGIN{print srand()}') + else + NOW=$(date "+%s") + fi + OLD_RELEASE=0 TIME_DIFFERENCE_CHECK=10368000 # 4 months RELEASE_PLUS_TIMEDIFF=$((${PROGRAM_RELEASE_TIMESTAMP} + ${TIME_DIFFERENCE_CHECK}))