harbian-audit/lib/utils.sh

1286 lines
34 KiB
Bash

# CIS Debian 7 Hardening Utility functions
#
# debian version check
#
is_centos_8()
{
if [ -r /etc/redhat-release ]; then
if [ $(grep -c "^CentOS.*8.[0-9].*" /etc/redhat-release) -eq 1 ]; then
debug "CentOS version is equal to 8"
FNRET=0
else
debug "CentOS version is less than 8"
FNRET=1
fi
else
debug "Current OS is not redhat/CentOS"
FNRET=2
fi
}
is_debian_ge_10()
{
# For debian11
DEBIAN11CODENAME="bullseye"
if [ -r /etc/debian_version ]; then
if [ $(grep -cw "^$DEBIAN11CODENAME" /etc/debian_version) -eq 1 ]; then
debug "Debian version is greater than or equal to 10"
FNRET=0
return
fi
if [ $(cat /etc/debian_version | awk -F"." '{print $1}') -ge 10 ]; then
debug "Debian version is greater than or equal to 10"
FNRET=0
else
debug "Debian version is less than 10"
FNRET=1
fi
else
debug "Current OS is not Debian."
FNRET=2
fi
}
is_debian_ge_9()
{
# For debian11
DEBIAN11CODENAME="bullseye"
if [ -r /etc/debian_version ]; then
if [ $(grep -cw "^$DEBIAN11CODENAME" /etc/debian_version) -eq 1 ]; then
debug "Debian version is greater than or equal to 10"
FNRET=0
return
fi
if [ $(cat /etc/debian_version | awk -F"." '{print $1}') -ge 9 ]; then
debug "Debian version is greater than or equal to 9"
FNRET=0
else
debug "Debian version is less than 9"
FNRET=1
fi
else
debug "Current OS is not Debian."
FNRET=2
fi
}
is_debian_9()
{
if [ -r /etc/debian_version ]; then
if $(cat /etc/debian_version | grep -q "^9.[0-9]"); then
debug "Debian version is 9.*."
FNRET=0
else
debug "Debian version is not 9.*."
FNRET=1
fi
else
debug "Current OS is not Debian."
FNRET=2
fi
}
is_debian_10()
{
if [ -r /etc/debian_version ]; then
if $(cat /etc/debian_version | grep -q "^10.[0-9]"); then
debug "Debian version is buster/10."
FNRET=0
else
debug "Debian version is not buster/10."
FNRET=1
fi
else
debug "Current OS is not Debian."
FNRET=2
fi
}
is_64bit_arch()
{
if $(uname -m | grep -q "64"); then
FNRET=0
debug "This machine architecture is 64 bit."
else
FNRET=1
debug "This machine architecture is not 64 bit."
fi
}
#
# Sysctl
#
has_sysctl_param_expected_result() {
local SYSCTL_PARAM=$1
local EXP_RESULT=$2
if [ "$($SUDO_CMD sysctl $SYSCTL_PARAM 2>/dev/null)" = "$SYSCTL_PARAM = $EXP_RESULT" ]; then
FNRET=0
elif [ $? = 255 ]; then
debug "$SYSCTL_PARAM does not exist"
FNRET=255
else
debug "$SYSCTL_PARAM should be set to $EXP_RESULT"
FNRET=1
fi
}
does_sysctl_param_exists() {
local SYSCTL_PARAM=$1
if [ "$($SUDO_CMD sysctl -a 2>/dev/null |grep "$SYSCTL_PARAM" -c)" = 0 ]; then
FNRET=1
else
FNRET=0
fi
}
set_sysctl_param() {
local SYSCTL_PARAM=$1
local VALUE=$2
debug "Setting $SYSCTL_PARAM to $VALUE"
if [ "$(sysctl -w $SYSCTL_PARAM=$VALUE 2>/dev/null)" = "$SYSCTL_PARAM = $VALUE" ]; then
echo "$SYSCTL_PARAM = $VALUE" >> /etc/sysctl.conf
FNRET=0
elif [ $? = 255 ]; then
debug "$SYSCTL_PARAM does not exist"
FNRET=255
else
warn "$SYSCTL_PARAM failed!"
FNRET=1
fi
}
#
# Dmesg
#
does_pattern_exist_in_dmesg() {
local PATTERN=$1
if $($SUDO_CMD dmesg | grep -qE "$PATTERN"); then
FNRET=0
else
FNRET=1
fi
}
#
# File
#
does_file_exist() {
local FILE=$1
if $SUDO_CMD [ -r $FILE ]; then
FNRET=0
else
FNRET=1
fi
}
has_file_correct_ownership() {
local FILE=$1
local USER=$2
local GROUP=$3
local USERID=$(id -u $USER)
local GROUPID=$(getent group $GROUP | cut -d: -f3)
debug "$SUDO_CMD stat -c '%u %g' $FILE"
if [ "$($SUDO_CMD stat -c "%u %g" $FILE)" = "$USERID $GROUPID" ]; then
FNRET=0
else
FNRET=1
fi
}
has_file_correct_permissions() {
local FILE=$1
local PERMISSIONS=$2
if [ -e $FILE ]; then
if [ $($SUDO_CMD stat -L -c "%a" $1) = "$PERMISSIONS" ]; then
FNRET=0
else
FNRET=1
fi
else
FNRET=1
info "$FILE is not exist!"
fi
}
does_pattern_exist_in_file() {
local FILE=$1
local PATTERN=$2
debug "Checking if $PATTERN is present in $FILE"
if $SUDO_CMD [ -r "$FILE" ] ; then
debug "$SUDO_CMD grep -qE -- '$PATTERN' $FILE"
if $($SUDO_CMD grep -qE -- "$PATTERN" $FILE); then
FNRET=0
else
FNRET=1
fi
else
debug "File $FILE is not readable!"
FNRET=2
fi
}
# Check after deleting blank lines and comment lines
does_valid_pattern_exist_in_file() {
local FILE=$1
local PATTERN=$2
debug "Checking if $PATTERN is present in $FILE"
if $SUDO_CMD [ -r "$FILE" ] ; then
debug "$SUDO_CMD sed '/^#/d' $FILE | sed '/^$/d' | grep -c '$PATTERN'"
if [ $($SUDO_CMD sed '/^#/d' $FILE | sed '/^$/d' | grep -c "$PATTERN") -gt 0 ]; then
FNRET=0
else
FNRET=1
fi
else
debug "File $FILE is not readable!"
FNRET=2
fi
}
add_end_of_file() {
local FILE=$1
local LINE=$2
debug "Adding $LINE at the end of $FILE"
backup_file "$FILE"
echo "$LINE" >> $FILE
}
add_line_file_before_pattern() {
local FILE=$1
local LINE=$2
local PATTERN=$3
backup_file "$FILE"
debug "Inserting $LINE before $PATTERN in $FILE"
PATTERN=$(sed 's@/@\\\/@g' <<< $PATTERN)
debug "sed -i '/$PATTERN/i $LINE' $FILE"
sed -i "/$PATTERN/i $LINE" $FILE
FNRET=0
}
add_line_file_after_pattern() {
local FILE=$1
local LINE=$2
local PATTERN=$3
backup_file "$FILE"
debug "Inserting $LINE before $PATTERN in $FILE"
PATTERN=$(sed 's@/@\\\/@g' <<< $PATTERN)
debug "sed -i '/$PATTERN/a $LINE' $FILE"
sed -i "/$PATTERN/a $LINE" $FILE
FNRET=0
}
add_line_file_after_pattern_lastline() {
local FILE=$1
local LINE=$2
local PATTERN=$3
local LASTLINE=-1
backup_file "$FILE"
debug "Inserting $LINE after $PATTERN in $FILE"
PATTERN=$(sed 's@/@\\\/@g' <<< $PATTERN)
if [ $(grep "^$PATTERN" $FILE -c) -gt 0 ]; then
LASTLINE=$(grep "$PATTERN" $FILE -n | sed -n '$p' | awk -F: '{print $1}')
debug "sed -i '$LASTLINE a $LINE' $FILE"
sed -i "$LASTLINE a $LINE" $FILE
FNRET=0
else
crit "$PATTERN is not exist in $FILE"
FNRET=1
fi
}
replace_in_file() {
local FILE=$1
local SOURCE=$2
local DESTINATION=$3
backup_file "$FILE"
debug "Replacing $SOURCE to $DESTINATION in $FILE"
SOURCE=$(sed 's@/@\\\/@g' <<< $SOURCE)
debug "sed -i 's/$SOURCE/$DESTINATION/g' $FILE"
sed -i "s/$SOURCE/$DESTINATION/g" $FILE
FNRET=0
}
delete_line_in_file() {
local FILE=$1
local PATTERN=$2
backup_file "$FILE"
debug "Deleting lines from $FILE containing $PATTERN"
PATTERN=$(sed 's@/@\\\/@g' <<< $PATTERN)
debug "sed -i '/$PATTERN/d' $FILE"
sed -i "/$PATTERN/d" $FILE
FNRET=0
}
#
# Users and groups
#
does_user_exist() {
local USER=$1
if $(getent passwd $USER >/dev/null 2>&1); then
FNRET=0
else
FNRET=1
fi
}
does_group_exist() {
local GROUP=$1
if $(getent group $GROUP >/dev/null 2>&1); then
FNRET=0
else
FNRET=1
fi
}
#
# Service Boot Checks
#
is_service_active() {
local SERVICE=$1
if [ $OS_RELEASE -eq 2 ]; then
FNRET=0
else
is_debian_ge_9
fi
if [ $FNRET = 0 ]; then
if [ $(systemctl is-active $SERVICE | grep -c "^active") -eq 1 ]; then
debug "Service $SERVICE is actived"
FNRET=0
else
debug "Service $SERVICE is inactived"
FNRET=1
fi
else
if [ $($SUDO_CMD find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then
debug "Service $SERVICE is enabled"
FNRET=0
else
debug "Service $SERVICE is disabled"
FNRET=1
fi
fi
}
is_service_enabled() {
local SERVICE=$1
if [ $OS_RELEASE -eq 2 ]; then
FNRET=0
else
is_debian_9
fi
if [ $FNRET = 0 ]; then
if [ $(systemctl is-enabled $SERVICE | grep -c "^enabled") -eq 1 ]; then
debug "Service $SERVICE is enabled"
FNRET=0
else
debug "Service $SERVICE is disabled"
FNRET=1
fi
else
if [ $($SUDO_CMD find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then
debug "Service $SERVICE is enabled"
FNRET=0
else
debug "Service $SERVICE is disabled"
FNRET=1
fi
fi
}
#
# Kernel Options checks
#
is_kernel_option_enabled() {
local KERNEL_OPTION="$1"
local MODULE_NAME=""
if [ $# -ge 2 ] ; then
MODULE_NAME="$2"
fi
if $SUDO_CMD [ -r "/proc/config.gz" ] ; then
RESULT=$($SUDO_CMD zgrep "^$KERNEL_OPTION=" /proc/config.gz) || :
elif $SUDO_CMD [ -r "/boot/config-$(uname -r)" ] ; then
RESULT=$($SUDO_CMD grep "^$KERNEL_OPTION=" "/boot/config-$(uname -r)") || :
fi
ANSWER=$(cut -d = -f 2 <<< "$RESULT")
if [ "x$ANSWER" = "xy" ]; then
debug "Kernel option $KERNEL_OPTION enabled"
FNRET=0
elif [ "x$ANSWER" = "xn" ]; then
debug "Kernel option $KERNEL_OPTION disabled"
FNRET=1
else
debug "Kernel option $KERNEL_OPTION not found"
FNRET=2 # Not found
fi
if $SUDO_CMD [ "$FNRET" -ne 0 -a -n "$MODULE_NAME" -a -d "/lib/modules/$(uname -r)" ] ; then
# also check in modules, because even if not =y, maybe
# the admin compiled it separately later (or out-of-tree)
# as a module (regardless of the fact that we have =m or not)
debug "Checking if we have $MODULE_NAME.ko"
local modulefile=$($SUDO_CMD find "/lib/modules/$(uname -r)/" -type f -name "$MODULE_NAME.ko")
if $SUDO_CMD [ -n "$modulefile" ] ; then
debug "We do have $modulefile!"
# ... but wait, maybe it's blacklisted? check files in /etc/modprobe.d/ for "blacklist xyz"
if grep -qRE "^\s*blacklist\s+$MODULE_NAME\s*$" /etc/modprobe.d/ ; then
debug "... but it's blacklisted!"
FNRET=1 # Not found (found but blacklisted)
# FIXME: even if blacklisted, it might be present in the initrd and
# be insmod from there... but painful to check :/ maybe lsmod would be enough ?
fi
FNRET=0 # Found!
fi
fi
}
#
# Mounting point
#
# Verify $1 is a partition declared in fstab
is_a_partition() {
local PARTITION=$1
FNRET=128
if $(grep "[[:space:]]*${PARTITION}[[:space:]].*" /etc/fstab | grep -vqE "^#"); then
debug "$PARTITION found in fstab"
FNRET=0
else
debug "Unable to find $PARTITION in fstab"
FNRET=1
fi
}
# Verify that $1 is mounted at runtime
is_mounted() {
local PARTITION=$1
if $(grep -q "[[:space:]]$1[[:space:]]" /proc/mounts); then
debug "$PARTITION found in /proc/mounts, it's mounted"
FNRET=0
else
debug "Unable to find $PARTITION in /proc/mounts"
FNRET=1
fi
}
# Verify $1 has the proper option $2 in fstab
has_mount_option() {
local PARTITION=$1
local OPTION=$2
if $(grep "[[:space:]]$1[[:space:]]" /etc/fstab | grep -vE "^#" | awk {'print $4'} | grep -q "$2"); then
debug "$OPTION has been detected in fstab for partition $PARTITION"
FNRET=0
else
debug "Unable to find $OPTION in fstab for partition $PARTITION"
FNRET=1
fi
}
# Verify option $2 in $1 service
has_mount_option_systemd() {
local SERVICENAME=$1
local OPTION=$2
if $(grep -i "options" "$SERVICENAME" | grep -vE "^#" | grep -q "$2"); then
debug "$OPTION has been detected in systemd service $SERVICENAME"
FNRET=0
else
debug "Unable to find $OPTION in systemd service $SERVICENAME"
FNRET=1
fi
}
# Verify $1 has the proper option $2 at runtime
has_mounted_option() {
local PARTITION=$1
local OPTION=$2
if $(grep "[[:space:]]$1[[:space:]]" /proc/mounts | awk {'print $4'} | grep -q "$2"); then
debug "$OPTION has been detected in /proc/mounts for partition $PARTITION"
FNRET=0
else
debug "Unable to find $OPTION in /proc/mounts for partition $PARTITION"
FNRET=1
fi
}
# Setup mount option in fstab
# Notice: The format of the entry in the fstab file must be in the format shown in the following example, otherwise an error may occur.
add_option_to_fstab() {
local PARTITION=$1
local OPTION=$2
debug "Setting $OPTION for $PARTITION in fstab"
backup_file "/etc/fstab"
# For example :
# local PARTITION="/home"
# local OPTION="nosuid"
# UUID=40327bc9-f9d1-5816-a312-df307cc8732e /home ext4 errors=remount-ro 0 2
# UUID=40327bc9-f9d1-5816-a312-df307cc8732e /home ext4 errors=remount-ro,nosuid 0 2
# debug "Sed command : sed -ie \"s;\(.*\)\(\s*\)\s\($PARTITION\)\s\(\s*\)\(\w*\)\(\s*\)\(\w*\)*;\1\2 \3 \4\5\6\7,$OPTION;\" /etc/fstab"
# sed -ie "s;\(^[^#].*${PARTITION}\)\(\s.*\)\(\s\w.*\)\(\s[0-2]\s*[0-2]\);\1\2\3,${OPTION}\4;" /etc/fstab
MOUNT_OPTION=$(grep -v "^#" /etc/fstab | awk '$2=="'${PARTITION}'" {print $4}')
CURLINE=$(grep -v "^#" /etc/fstab -n | grep "${PARTITION}" | awk -F: '{print $1}')
#This case is for option of starting with "no", example: nosuid noexec nodev
NOTNOOPTION=$(echo $OPTION | cut -c 3-)
if [ "${MOUNT_OPTION}" == "defaults" ]; then
if [ "$OPTION" == "noexec" ]; then
NEWOP='rw,nosuid,nodev,noexec,auto,async'
else
NEWOP='rw,nosuid,nodev,auto,async'
fi
sed -i "${CURLINE}s/$MOUNT_OPTION/$NEWOP/" /etc/fstab
#This case is for option of starting with "no", example: nosuid noexec nodev
elif [ $(echo $MOUNT_OPTION | grep -cw ${NOTNOOPTION}) -gt 0 ]; then
sed -i "${CURLINE}s/${NOTNOOPTION}/${OPTION}/" /etc/fstab
elif [ $(echo $MOUNT_OPTION | grep -cw $OPTION) -eq 0 ]; then
sed -i "${CURLINE}s/${MOUNT_OPTION}/${MOUNT_OPTION},${OPTION}/" /etc/fstab
fi
}
remount_partition() {
local PARTITION=$1
debug "Remounting $PARTITION"
mount -o remount $PARTITION
}
# Setup mount option in systemd
add_option_to_systemd() {
local SERVICEPATH=$1
local OPTION=$2
local SERVICENAME=$3
debug "Setting $OPTION for in systemd"
backup_file "$SERVICEPATH"
if [ $OS_RELEASE -eq 2 ]; then
# For CentOS
warn "This item to apply requires reboot OS."
else
# For debian
systemctl stop $SERVICENAME
fi
# For example :
# Options=mode=1777,strictatime,nosuid
# Options=mode=1777,strictatime,nosuid,nodev
#debug "Sed command : sed -ie "s;\(^Options.*=mode=[1,2,4,7][1,2,4,7][1,2,4,7][1,2,4,7].*\);\1,$OPTION;\" $SERVICEPATH"
sed -ie "s;\(^Options.*=mode=[1,2,4,7][1,2,4,7][1,2,4,7][1,2,4,7].*\);\1,$OPTION;" $SERVICEPATH
systemctl daemon-reload
}
remount_partition_by_systemd() {
local SERVICENAME=$1
local PARTITION=$2
debug "Remounting $PARTITION by systemd"
if [ $OS_RELEASE -eq 2 ]; then
# For CentOS
warn "This item to apply requires reboot OS."
else
# For debian
systemctl start $SERVICENAME
fi
}
#
# APT
#
apt_update_if_needed()
{
if [ -e /var/cache/apt/pkgcache.bin ]
then
UPDATE_AGE=$(( $(date +%s) - $(stat -c '%Y' /var/cache/apt/pkgcache.bin) ))
if [ $UPDATE_AGE -gt 3600 ]
then
# update too old, refresh database
$SUDO_CMD apt-get update >/dev/null 2>/dev/null
fi
else
$SUDO_CMD apt-get update >/dev/null 2>/dev/null
fi
}
apt_check_updates()
{
local NAME="$1"
local DETAILS="/dev/shm/${NAME}"
$SUDO_CMD apt-get upgrade -s 2>/dev/null | grep -E "^Inst" > $DETAILS || :
local COUNT=$(wc -l < "$DETAILS")
FNRET=128 # Unknown function return result
RESULT="" # Result output for upgrade
if [ $COUNT -gt 0 ]; then
RESULT="There is $COUNT updates available :\n$(cat $DETAILS)"
FNRET=1
else
RESULT="OK, no updates available"
FNRET=0
fi
rm $DETAILS
}
apt_install()
{
local PACKAGE=$1
DEBIAN_FRONTEND='noninteractive' apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install $PACKAGE -y
FNRET=0
}
yum_install()
{
local PACKAGE=$1
yum install -y $PACKAGE
FNRET=0
}
install_package()
{
if [ $OS_RELEASE -eq 1 ]; then
local PACKAGE=$1
apt_install $PACKAGE
elif [ $OS_RELEASE -eq 2 ]; then
local PACKAGE=$1
yum_install $PACKAGE
else
warn "Current OS is not support!"
fi
FNRET=0
}
#
# Return 0 if a package is installed
#
is_pkg_installed()
{
PKG_NAME=$1
if [ $OS_RELEASE -eq 2 ]; then
if [ $(yum list installed | grep -c "^$PKG_NAME\.") -gt 0 ]; then
debug "$PKG_NAME is installed"
FNRET=0
else
debug "$PKG_NAME is not installed"
FNRET=1
fi
else
if $(dpkg -s $PKG_NAME 2> /dev/null | grep -q '^Status: install ') ; then
debug "$PKG_NAME is installed"
FNRET=0
else
debug "$PKG_NAME is not installed"
FNRET=1
fi
fi
}
verify_integrity_all_packages()
{
if [ $OS_RELEASE -eq 2 ]; then
rpm -Va > /dev/shm/yum_verify_ret
COUNT=$(cat /dev/shm/yum_verify_ret | wc -l )
if [ $COUNT -gt 0 ]; then
debug "Verify integrity all packages is fail"
cat /dev/shm/yum_verify_ret
rm /dev/shm/yum_verify_ret
FNRET=1
else
debug "Verify integrity all packages is OK"
FNRET=0
fi
else
dpkg -V > /dev/shm/dpkg_verify_ret
if [ $(cat /dev/shm/dpkg_verify_ret | wc -l) -gt 0 ]; then
debug "Verify integrity all packages is fail"
cat /dev/shm/dpkg_verify_ret
FNRET=1
else
debug "Verify integrity all packages is OK"
FNRET=0
fi
fi
}
# Check paramer with value
# example : minlen = 9
# ruturn: 0 1 2 3
check_param_pair_by_value ()
{
FILENAME=$1
OPTION=$2
COMPARE=$3
OP_VALUE=$4
#Example:
# FILENAME="/etc/security/pwquality.conf"
# OPTION="minlen"
# COMPARE="ge"
# OP_VALUE=15
if [ -f "$FILENAME" ];then
COUNT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $FILENAME | grep "^$OPTION[[:space:]]=[[:space:]]" | wc -l)
if [ $COUNT -eq 1 ]; then
debug "$OPTION is conf"
RESULT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $FILENAME | grep "^$OPTION[[:space:]]=[[:space:]]")
if [ "$(echo $RESULT | awk -F'= ' '{print $2}')" "-$COMPARE" "$OP_VALUE" ]; then
debug "$OPTION conf is right."
FNRET=0
else
debug "$OPTION conf is not right."
FNRET=1
fi
else
debug "$OPTION is not conf of $FILENAME"
FNRET=2
fi
else
debug "$FILENAME is not exist"
FNRET=3
fi
}
check_param_pair_by_pam()
{
LOCATION=$1
KEYWORD=$2
OPTION=$3
COMPARE=$4
CONDITION=$5
#Example:
#LOCATION="/etc/pam.d/common-password"
#LOCATION="/etc/pam.d/login"
#KEYWORD="pam_cracklib.so"
#OPTION="ocredit"
#COMPARE="gt"
#CONDITION="-1"
if [ -f "$LOCATION" ];then
RESULT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $LOCATION | grep -w "$KEYWORD.*$OPTION" | wc -l)
if [ "$RESULT" -eq 1 ]; then
debug "$KEYWORD $OPTION is conf"
cndt_value=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $LOCATION | grep "$KEYWORD.*$OPTION" | tr "\t" " " | tr " " "\n" | sed -n "/$OPTION/p" | awk -F "=" '{print $2}')
if [ "$cndt_value" "-$COMPARE" "$CONDITION" ]; then
debug "$cndt_value -$COMPARE $CONDITION is ok"
FNRET=0
elif [ "$cndt_value" -eq 0 ]; then
debug "$cndt_value -eq 0, is not ok"
FNRET=5
else
debug "$cndt_value -$COMPARE $CONDITION is not ok"
FNRET=5
fi
else
debug "$KEYWORD $OPTION is not conf"
FNRET=4
fi
else
debug "$LOCATION is not exist"
FNRET=3
fi
}
# Only check option name
check_no_param_option_by_pam()
{
KEYWORD=$1
OPTION=$2
LOCATION=$3
#Example:
#KEYWORD="pam_unix.so"
#OPTION="sha512"
#LOCATION="/etc/pam.d/common-password"
if [ -f "$LOCATION" ];then
RESULT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $LOCATION | grep "$KEYWORD.*$OPTION" | wc -l)
if [ "$RESULT" -eq 1 ]; then
debug "$KEYWORD $OPTION is conf"
FNRET=0
else
debug "$KEYWORD $OPTION is not conf"
FNRET=4
fi
else
debug "$LOCATION is not exist"
FNRET=3
fi
}
# Add password check option
add_option_to_password_check()
{
#Example:
#local PAMPWDFILE="/etc/pam.d/common-password"
#local KEYWORD="pam_cracklib.so"
#local OPTIONSTR="retry=3"
local PAMPWDFILE=$1
local KEYWORD=$2
local OPTIONSTR=$3
debug "Setting $OPTIONSTR for $KEYWORD"
backup_file "$PAMPWDFILE"
# For example :
# password requisite pam_cracklib.so minlen=8 difok=3
# password requisite pam_cracklib.so minlen=8 difok=3 retry=3
sed -i "s;\(^password.*$KEYWORD.*\);\1 $OPTIONSTR;" $PAMPWDFILE
}
# Add session check option
add_option_to_session_check()
{
#Example:
#local PAMPWDFILE="/etc/pam.d/login"
#local KEYWORD="pam_lastlog.so"
#local OPTIONSTR="showfailed"
local PAMPWDFILE=$1
local KEYWORD=$2
local OPTIONSTR=$3
debug "Setting $OPTIONSTR for $KEYWORD"
backup_file "$PAMPWDFILE"
# For example :
# password requisite pam_cracklib.so minlen=8 difok=3
# password requisite pam_cracklib.so minlen=8 difok=3 retry=3
sed -i "s;\(^session.*$KEYWORD.*\);\1 $OPTIONSTR;" $PAMPWDFILE
}
# Add auth check option
add_option_to_auth_check()
{
#Example:
#local PAMPWDFILE="/etc/pam.d/common-auth"
#local KEYWORD="pam_cracklib.so"
#local OPTIONSTR="retry=3"
local PAMPWDFILE=$1
local KEYWORD=$2
local OPTIONSTR=$3
debug "Setting $OPTIONSTR for $KEYWORD"
backup_file "$PAMPWDFILE"
# For example :
# password requisite pam_cracklib.so minlen=8 difok=3
# password requisite pam_cracklib.so minlen=8 difok=3 retry=3
sed -i "s;\(^auth.*$KEYWORD.*\);\1 $OPTIONSTR;" $PAMPWDFILE
}
# Reset password check option value when option is not set a correct value
reset_option_to_password_check()
{
#Example:
#local PAMPWDFILE="/etc/pam.d/common-password"
#local KEYWORD="pam_cracklib.so"
#local OPTIONNAME="retry"
#local OPTIONVAL="3"
local PAMPWDFILE=$1
local KEYWORD=$2
local OPTIONNAME=$3
local OPTIONVAL=$4
debug "Setting $OPTION for $KEYWORD reset option value to $OPTIONVAL"
backup_file "$PAMPWDFILE"
# For example :
# password requisite pam_cracklib.so minlen=8 difok=3 retry=1
# password requisite pam_cracklib.so minlen=8 difok=3 retry=3
cndt_value=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $PAMPWDFILE | grep "$KEYWORD.*$OPTIONNAME" | tr "\t" " " | tr " " "\n" | sed -n "/$OPTIONNAME/p" | awk -F "=" '{print $2}')
sed -i "s/${OPTIONNAME}=${cndt_value}/${OPTIONNAME}=${OPTIONVAL}/" $PAMPWDFILE
}
# Reset auth check option value when option is not set a correct value
reset_option_to_auth_check()
{
#Example:
#local PAMPWDFILE="/etc/pam.d/common-password"
#local KEYWORD="pam_cracklib.so"
#local OPTIONNAME="retry"
#local OPTIONVAL="3"
local PAMPWDFILE=$1
local KEYWORD=$2
local OPTIONNAME=$3
local OPTIONVAL=$4
debug "Setting $OPTION for $KEYWORD reset option value to $OPTIONVAL"
backup_file "$PAMPWDFILE"
# For example :
# password requisite pam_cracklib.so minlen=8 difok=3 retry=1
# password requisite pam_cracklib.so minlen=8 difok=3 retry=3
sed -i "s/${OPTIONNAME}=.*/${OPTIONNAME}=${OPTIONVAL}/" $PAMPWDFILE
}
# Only check option name
check_auth_option_nullok_by_pam()
{
KEYWORD=$1
OPTION1=$2
OPTION2=$3
LOCATION="/etc/pam.d/common-auth"
#Example:
#KEYWORD="pam_unix.so"
#OPTION1="nullok"
#OPTION2="nullok_secure"
if [ -f "$LOCATION" ];then
RESULT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $LOCATION | grep "$KEYWORD.*$OPTION2" | wc -l)
if [ "$RESULT" -eq 1 ]; then
debug "$KEYWORD $OPTION2 is conf, that is error conf"
FNRET=5
else
debug "$KEYWORD $OPTION2 is not conf, that is ok"
RESULT=$(sed -e '/^#/d' -e '/^[ \t][ \t]*#/d' -e 's/#.*$//' -e '/^$/d' $LOCATION | grep "$KEYWORD.*$OPTION1" | wc -l)
if [ "$RESULT" -eq 1 ]; then
debug "$KEYWORD $OPTION1 is conf, that is error conf"
FNRET=4
else
debug "$KEYWORD $OPTION1 is not conf, that is ok"
FNRET=0
fi
fi
else
debug "$LOCATION is not exist"
FNRET=3
fi
}
# Ensure is set accept for INPUT of loopback traffic
ensure_lo_traffic_input_is_accept()
{
IPS4=$(which iptables)
IPS6=$(which ip6tables)
# Check the loopback interface to accept INPUT traffic.
version=$1
if [ $version == 'IPS4' ]; then
if [ $(${IPS4} -S | grep -c "^\-A INPUT \-i lo \-j ACCEPT") -ge 1 -o $(${IPS4} -S | grep -c "^\-A INPUT \-i 127.0.0.1 \-j ACCEPT") -ge 1 ]; then
debug "Ip4tables loopback traffic INPUT has configured!"
FNRET=0
else
debug "Ip4tables: loopback traffic INPUT is not configured!"
FNRET=1
fi
else
if [ $(${IPS6} -S | grep -c "^\-A INPUT \-i lo \-j ACCEPT") -ge 1 -o $(${IPS6} -S | grep -c "^\-A INPUT \-i ::/0 \-j ACCEPT") -ge 1 ]; then
debug "Ip6tables loopback traffic INPUT has configured!"
FNRET=0
else
debug "Ip6tables: loopback traffic INPUT is not configured!"
FNRET=1
fi
fi
}
# Ensure is set accept for OUTPUT of loopback traffic
ensure_lo_traffic_output_is_accept()
{
IPS4=$(which iptables)
IPS6=$(which ip6tables)
# Check the loopback interface to accept OUTPUT traffic.
version=$1
if [ $version == 'IPS4' ]; then
if [ $(${IPS4} -S | grep -c "^\-A OUTPUT \-o lo \-j ACCEPT") -ge 1 -o $(${IPS4} -S | grep -c "^\-A OUTPUT \-o 127.0.0.1 \-j ACCEPT") -ge 1 ]; then
debug "Ip4tables loopback traffic OUTPUT has configured!"
FNRET=0
else
debug "Ip4tables: loopback traffic OUTPUT is not configured!"
FNRET=1
fi
else
if [ $(${IPS6} -S | grep -c "^\-A OUTPUT \-o lo \-j ACCEPT") -ge 1 -o $(${IPS6} -S | grep -c "^\-A OUTPUT \-o ::/0 \-j ACCEPT") -ge 1 ]; then
debug "Ip6tables loopback traffic OUTPUT has configured!"
FNRET=0
else
debug "Ip6tables: loopback traffic OUTPUT is not configured!"
FNRET=1
fi
fi
}
# Ensure is set deny for other interfaces INPUT of loopback traffic
ensure_lo_traffic_other_if_input_is_deny()
{
IPS4=$(which iptables)
IPS6=$(which ip6tables)
# all other interfaces to deny traffic to the loopback network.
version=$1
if [ $version == 'IPS4' ]; then
if [ $(${IPS4} -S | grep -c "^\-A INPUT \-s 127.0.0.0/8 \-j DROP") -eq 0 ]; then
debug "Ip4tables: loopback traffic INPUT deny from 127.0.0.0/8 is not configured!"
FNRET=1
else
debug "Ip4tables loopback traffic INPUT deny from 127.0.0.0/8 has configured!"
FNRET=0
fi
else
if [ $(${IPS6} -S | grep -c "^\-A INPUT \-s ::1 \-j DROP") -ge 0 ]; then
debug "Ip6tables: loopback traffic INPUT deny from ::1 is not configured!"
FNRET=1
else
debug "Ip6tables loopback traffic INPUT deny from ::1 has configured!"
FNRET=0
fi
fi
}
#Ensure is set accept for all outbound
check_outbound_connect_is_accept()
{
PATTERN="\-\-state NEW,ESTABLISHED \-j ACCEPT"
IPS4=$(which iptables)
IPS6=$(which ip6tables)
# $1 maybe is: tcp udp icmp
proto=$1
version=$2
if [ $version == 'IPS4' ]; then
if [ $(${IPS4} -S | grep "^\-A OUTPUT" | grep "\-p ${proto}" | grep -c "$PATTERN") -eq 0 ]; then
debug "Iptables: Protocol $proto outbound is not configured!"
FNRET=1
else
debug "Iptables: Protocol $proto outbound is configured!"
FNRET=0
fi
else
if [ $(${IPS6} -S | grep "^\-A OUTPUT" | grep "\-p ${proto}" | grep -c "$PATTERN") -eq 0 ]; then
debug "Ip6tables: Protocol $proto outbound is not configured!"
FNRET=1
else
debug "Ip6tables: Protocol $proto outbound is configured!"
FNRET=0
fi
fi
}
#Ensure is set accept for input with ESTABLISHED
check_input_with_established_is_accept()
{
PATTERN="\-\-state ESTABLISHED \-j ACCEPT"
IPS4=$(which iptables)
IPS6=$(which ip6tables)
# $1 maybe is: tcp udp icmp
proto=$1
version=$2
if [ $version == 'IPS4' ]; then
if [ $(${IPS4} -S | grep "^\-A INPUT" | grep "\-p ${proto}" | grep -c "$PATTERN") -eq 0 ]; then
debug "Iptables: Protocol $proto INPUT is not configured!"
FNRET=1
else
debug "Iptables: Protocol $proto INPUT is configured!"
FNRET=0
fi
else
if [ $(${IPS6} -S | grep "^\-A INPUT" | grep "\-p ${proto}" | grep -c "$PATTERN") -eq 0 ]; then
debug "Ip6tables: Protocol $proto INPUT is not configured!"
FNRET=1
else
debug "Ip6tables: Protocol $proto INPUT is configured!"
FNRET=0
fi
fi
}
# for: Create file if parent dir is not exist
extend_touch_file()
{
NEWFILEALLPATH=$1
if [ ! -d $(dirname ${NEWFILEALLPATH}) ]; then
mkdir -p "$(dirname ${NEWFILEALLPATH})"
touch ${NEWFILEALLPATH}
else
touch ${NEWFILEALLPATH}
fi
}
# Check ipv6 is enable
check_ipv6_is_enable()
{
if [ $(ip -6 addr | wc -l) -gt 0 ]; then
debug "Ipv6 is enabled."
FNRET=0
else
debug "Ipv6 is disabled."
FNRET=1
fi
}
check_auditd_is_immutable_mode()
{
if [ $(auditctl -s | head -n 1 | awk '{print $2}') -eq 2 ]; then
warn "The auditd system is in immutable mode, no rule changes allowed. So must need reboot after adding/modifying the auditd rule!"
else
eval $(pkill -HUP -P 1 auditd)
fi
}
#
# yum
#
# FNRET values:
# 100: need update
# 0: not need update
# 1: error
yum_check_updates()
{
FNRET=$($SUDO_CMD yum check-update > /dev/null; echo $?)
if [ $FNRET -eq 100 ]; then
# update too old, refresh database
$SUDO_CMD yum makecache >/dev/null 2>/dev/null
fi
}
# Check path of audit rule is exist, return 0 if path string is not NULL, else return 1
# Example:
# Process only the following format:
# AUDITRULE="-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged-passwd" or
# AUDITRULE="-a always,exit -F dir=/home/ -F auid>=1000 -F auid!=4294967295 -k privileged-passwd" or
# AUDITRULE="-w /home/ -k privileged-passwd"
# Please manually execute apt-file (Debian) / yum Provides (redhat) to ensure that the path already exists in the repository.
# example: apt-file search /usr/bin/passwd
# freedom-maker: /usr/bin/passwd-in-image
# passwd: /usr/bin/passwd
check_audit_path ()
{
AUDITRULE=$1
# Check -w style, for example: "-w /etc/shadow -p wa" "-w /etc/ -p wa"
if [[ $AUDITRULE =~ "-w" ]]; then
RESULT=$(echo $AUDITRULE | awk '{print $2}')
if [ -f $(eval echo $RESULT) -o -d $(eval echo $RESULT) ]; then
debug "File $RESULT is exist!"
FNRET=0
else
warn "File $RESULT is not exist!"
FNRET=1
fi
# Check -F style, for example: "-a always,exit -F path=/etc/shadow -F perm=wa" "-a always,exit -F dir=/etc/ -F perm=wa"
elif [ $(echo $AUDITRULE | grep -c "\-F.*path=") -eq 1 -o $(echo $AUDITRULE | grep -c "\-F.*dir=") -eq 1 ]; then
RESULT=$(echo $AUDITRULE | awk -F"-F" '{print $2}' | awk -F"=" '{print $2}')
if [ -f $(eval echo $RESULT) -o -d $(eval echo $RESULT) ]; then
debug "File $RESULT is exist!"
FNRET=0
else
warn "File $RESULT is not exist!"
FNRET=1
fi
else
info "This rule is not including path or dir."
FNRET=0
fi
}
# For CentOS 8
# Reference: https://access.redhat.com/solutions/3906701
tcp_wrappers_warn ()
{
warn "The package(tcp_wrappers) has been deprecated in RHEL 7 and therefore it will not be avaliable in RHEL 8 or later RHEL release."
}
uninstall_pkg ()
{
PKGNAME=$1
if [ $OS_RELEASE -eq 1 ]; then
apt-get -y purge --autoremove $PKGNAME
elif [ $OS_RELEASE -eq 2 ]; then
yum -y autoremove $PKGNAME
fi
}
# Check apparmor is active by aa-status
# Only support Debian
check_aa_status ()
{
APPARMOR_STATUS='/usr/sbin/aa-status'
if [ -f "$APPARMOR_STATUS" ]; then
$APPARMOR_STATUS > /dev/null 2>&1
case $? in
0) info "AppArmor is enabled and policy is loaded."
FNRET=0
;;
1) info "AppArmor is not enabled/loaded."
FNRET=1
;;
2) info "AppArmor enabled but no policy is loaded."
FNRET=2
;;
3) info "AppArmor control files aren't available under /sys/kernel/security/."
FNRET=3
;;
4) info "The user running the script doesn't have enough privileges to read the AppArmor control files."
FNRET=4
;;
esac
else
info "$APPARMOR_STATUS is not exist!"
FNRET=5
fi
}
# Check sshd access limit
# If not exist key of above, it's fail beacause default is everyone to allow
# Example: $1='AllowUsers' $2='AllowUsers[[:space:]]*\*'
check_sshd_access_limit ()
{
if [ $(sshd -T | grep -ic $1) -eq 1 ]; then
if [ $(sshd -T | grep -ic $2) -eq 1 ]; then
debug "$1 is not set limit!"
FNRET=2
else
debug "$1 has set limit!"
FNRET=0
fi
else
debug "Arguments $1 is not exist! By default, login is allowed for all."
FNRET=1
fi
}
# Check sshd conf for one value sshd -T return 'keyword value' pairs
# If the value of keyword is equal $2, return 0
# If the keywork does not exist in the sshd runtime configuration, return 1
# If the value of keyword is not equal $2, return 2
# Example: $1='PermitRootLogin' $2='no'
check_sshd_conf_for_one_value_runtime ()
{
COUNT=$(sshd -T | grep -i $1 | wc -l)
if [ $COUNT -eq 0 ]; then
debug "The keyword $1 does not exist in the sshd runtime configuration."
FNRET=1
else
RUNTIMEVALUE=$(sshd -T | grep -i $1 | awk '{print $2}')
if [ "$RUNTIMEVALUE" = "$2" ]; then
debug "The value of keyword $1 has set to $2, it's correct."
FNRET=0
else
debug "The value of keyword $1 is not set to $2, it's incorrect."
FNRET=2
fi
fi
}
# Check blacklist module set of /etc/modprobe.d/*
# If set, return 0; else return 1
# Example: $1='nf_nat_sip'
check_blacklist_module_set ()
{
MODPROBE_CONF_FILE_PATTERN="/etc/modprobe.d/*"
COUNT=$(grep -w $1 -r $MODPROBE_CONF_FILE_PATTERN | grep "^blacklist" | wc -l)
if [ $COUNT -ge 1 ]; then
debug "$1 has set in $MODPROBE_CONF_FILE_PATTERN"
FNRET=0
else
debug "$1 is not set in $MODPROBE_CONF_FILE_PATTERN"
FNRET=1
fi
}