diff --git a/bin/hardening/password_dictcheck_enabled.sh b/bin/hardening/password_dictcheck_enabled.sh new file mode 100755 index 0000000..5957304 --- /dev/null +++ b/bin/hardening/password_dictcheck_enabled.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure password dictionary check is enabled (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure password dictionary check is enabled" +# 'dictcheck=0' will disable the dictionnary check +EXPECTED_VALUE=1 + +# This function will be called if the script status is on enabled / audit mode +audit() { + QUALITY_VALID=1 + DICTCHECK_IN_PAM=1 + + # order of override from strongest to latest: + # - /etc/pam.d/* + # - /etc/security/pwquality.conf + # - /etc/security/pwquality.conf.d/*.conf + # "It is recommended that settings be configured in a .conf file in the /etc/security/pwquality.conf.d/ directory for clarity, convenience, and durability." + + local dictcheck_value="" + DICTCHECK_FILE="/etc/security/pwquality.conf" + + if [ -d /etc/security/pwquality.conf.d ]; then + if grep -E "^[[:space:]]?dictcheck" /etc/security/pwquality.conf.d/*.conf >/dev/null 2>&1; then + # if set in many places, the latest one is the one used + DICTCHECK_FILE=$(grep -lE "^[[:space:]]?dictcheck" | sort -n | tail -n 1) + fi + fi + + # maybe absent from /etc/security/pwquality.conf + if grep -E "^[[:space:]]?dictcheck" "$DICTCHECK_FILE" >/dev/null 2>&1; then + dictcheck_value=$(grep -E "^[[:space:]]?dictcheck" "$DICTCHECK_FILE" | awk -F '=' '{print $2}' | sed 's/\ *//g') + info "current 'pwquality dictcheck' value = $dictcheck_value" + + if [ "$dictcheck_value" -eq "$EXPECTED_VALUE" ]; then + QUALITY_VALID=0 + fi + fi + + for file in /usr/share/pam-configs/*; do + if grep -Pl -- '\bpam_pwquality\.so\h+([^#\n\r]+\h+)?dictcheck\b' "$file" >/dev/null 2>&1; then + DICTCHECK_IN_PAM=0 + break + fi + done + + if [ "$QUALITY_VALID" -eq 0 ]; then + ok "pwquality 'dictcheck' value is correctly configured" + else + crit "pwquality 'dictcheck' value is not correctly configured" + fi + + if [ "$DICTCHECK_IN_PAM" -eq 0 ]; then + crit "pwquality 'dictcheck' is overriden in pam configuration" + fi + +} + +# This function will be called if the script status is on enabled mode +apply() { + if [ "$QUALITY_VALID" -ne 0 ]; then + sed -E -i '/^[[:space:]]?dictcheck/d' "$DICTCHECK_FILE" + echo "dictcheck=$EXPECTED_VALUE" >>"$DICTCHECK_FILE" + fi + + if [ "$DICTCHECK_IN_PAM" -eq 0 ]; then + for file in /usr/share/pam-configs/*; do + if grep -Pl -- '\bpam_pwquality\.so\h+([^#\n\r]+\h+)?dictcheck\b' "$file" >/dev/null 2>&1; then + sed -E -i 's/dictcheck[[:space:]]?=[[:space:]]?[0-9]+//g' "$file" + fi + done + fi + +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/password_number_changed_chars.sh b/bin/hardening/password_number_changed_chars.sh new file mode 100755 index 0000000..638e03e --- /dev/null +++ b/bin/hardening/password_number_changed_chars.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure password number of changed characters is configured (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure password number of changed characters is configured" + +OPTIONS='' + +# This function will be called if the script status is on enabled / audit mode +audit() { + QUALITY_VALID=1 + DIFOK_IN_PAM=1 + + # order of override from strongest to latest: + # - /etc/pam.d/* + # - /etc/security/pwquality.conf + # - /etc/security/pwquality.conf.d/*.conf + # "It is recommended that settings be configured in a .conf file in the /etc/security/pwquality.conf.d/ directory for clarity, convenience, and durability." + expected_value=$(awk -F '=' '{print $2}' <<<"$OPTIONS") + + local difok_value="" + DIFOK_FILE="/etc/security/pwquality.conf" + + if [ -d /etc/security/pwquality.conf.d ]; then + if grep -E "^[[:space:]]?difok" /etc/security/pwquality.conf.d/*.conf >/dev/null 2>&1; then + # if set in many places, the latest one is the one used + DIFOK_FILE=$(grep -lE "^[[:space:]]?difok" | sort -n | tail -n 1) + fi + fi + + # maybe absent from /etc/security/pwquality.conf + if grep -E "^[[:space:]]?difok" "$DIFOK_FILE" >/dev/null 2>&1; then + difok_value=$(grep -E "^[[:space:]]?difok" "$DIFOK_FILE" | awk -F '=' '{print $2}' | sed 's/\ *//g') + info "current 'pwquality difok' value = $difok_value" + + if [ "$difok_value" -eq "$expected_value" ]; then + QUALITY_VALID=0 + fi + fi + + for file in /usr/share/pam-configs/*; do + if grep -Pl -- '\bpam_pwquality\.so\h+([^#\n\r]+\h+)?difok\b' "$file" >/dev/null 2>&1; then + DIFOK_IN_PAM=0 + break + fi + done + + if [ "$QUALITY_VALID" -eq 0 ]; then + ok "pwquality 'difok' value is correctly configured" + else + crit "pwquality 'difok' value is not correctly configured" + fi + + if [ "$DIFOK_IN_PAM" -eq 0 ]; then + crit "pwquality 'difok' is overriden in pam configuration" + fi + +} + +# This function will be called if the script status is on enabled mode +apply() { + if [ "$QUALITY_VALID" -ne 0 ]; then + sed -E -i '/^[[:space:]]?difok/d' "$DIFOK_FILE" + echo "$OPTIONS" >>"$DIFOK_FILE" + fi + + if [ "$DIFOK_IN_PAM" -eq 0 ]; then + for file in /usr/share/pam-configs/*; do + if grep -Pl -- '\bpam_pwquality\.so\h+([^#\n\r]+\h+)?difok\b' "$file" >/dev/null 2>&1; then + sed -E -i 's/difok[[:space:]]?=[[:space:]]?[0-9]+//g' "$file" + fi + done + fi + +} + +# This function will create the config file for this check with default values +create_config() { + cat </dev/null 2>&1; then + QUALITY_VALID=1 + break + fi + done + + if grep -PHsi -- '^\h*password\h+[^#\n\r]+\h+pam_pwquality\.so\h+([^#\n\r]+\h+)?enforcing=0\b' /etc/pam.d/common-password >/dev/null; then + DISABLED_IN_PAM=0 + QUALITY_VALID=1 + fi + + if [ "$QUALITY_VALID" -eq 0 ]; then + ok "password quality is enforced" + else + crit "password quality is not enforced" + fi + +} + +# This function will be called if the script status is on enabled mode +apply() { + if [ "$QUALITY_VALID" -ne 0 ]; then + for file in /usr/share/pam-configs/* /etc/security/pwquality.conf; do + if grep -Psi -- '^\h*enforcing\h*=\h*0\b' "$file" >/dev/null 2>&1; then + info "Commenting 'enforcing=0' in $file" + sed -ri 's/^\s*enforcing\s*=\s*0/# &/' "$file" + fi + done + fi + + if [ "$DISABLED_IN_PAM" -ne 1 ]; then + info "Removing 'enforcing=0' in /etc/pam.d/common-password" + sed -E -i 's/enforcing[[:space:]]?=[[:space:]]?0//g' /etc/pam.d/common-password + fi + +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/bin/hardening/password_quality_enforced_for_root.sh b/bin/hardening/password_quality_enforced_for_root.sh new file mode 100755 index 0000000..a97702a --- /dev/null +++ b/bin/hardening/password_quality_enforced_for_root.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# run-shellcheck +# +# CIS Debian Hardening +# + +# +# Ensure password quality is enforced for the root user (Automated) +# + +set -e # One error, it's over +set -u # One variable unset, it's over + +# shellcheck disable=2034 +HARDENING_LEVEL=2 +# shellcheck disable=2034 +DESCRIPTION="Ensure password quality is enforced for the root user" + +# This function will be called if the script status is on enabled / audit mode +audit() { + QUALITY_VALID=1 + + # order of override from strongest to latest: + # - /etc/pam.d/* + # - /etc/security/pwquality.conf + # - /etc/security/pwquality.conf.d/*.conf + # "It is recommended that settings be configured in a .conf file in the /etc/security/pwquality.conf.d/ directory for clarity, convenience, and durability." + + if [ -d /etc/security/pwquality.conf.d ]; then + for file in /etc/security/pwquality.conf.d/*.conf; do + if grep -E "^[[:space:]]?enforce_for_root" "$file" >/dev/null 2>&1; then + QUALITY_VALID=0 + ok "'pwquality' is enforced in '$file'" + fi + done + fi + + if grep -E "^[[:space:]]?enforce_for_root" /etc/security/pwquality.conf >/dev/null 2>&1; then + ok "'pwquality' is enforced for root in '/etc/security/pwquality.conf'" + fi + +} + +# This function will be called if the script status is on enabled mode +apply() { + if [ "$QUALITY_VALID" -ne 0 ]; then + echo "enforce_for_root" >>/etc/security/pwquality.conf + fi +} + +# This function will check config parameters required +check_config() { + : +} + +# Source Root Dir Parameter +if [ -r /etc/default/cis-hardening ]; then + # shellcheck source=../../debian/default + . /etc/default/cis-hardening +fi +if [ -z "$CIS_LIB_DIR" ]; then + echo "There is no /etc/default/cis-hardening file nor cis-hardening directory in current environment." + echo "Cannot source CIS_LIB_DIR variable, aborting." + exit 128 +fi + +# Main function, will call the proper functions given the configuration (audit, enabled, disabled) +if [ -r "${CIS_LIB_DIR}"/main.sh ]; then + # shellcheck source=../../lib/main.sh + . "${CIS_LIB_DIR}"/main.sh +else + echo "Cannot find main.sh, have you correctly defined your root directory? Current value is $CIS_LIB_DIR in /etc/default/cis-hardening" + exit 128 +fi diff --git a/tests/hardening/password_dictcheck_enabled.sh b/tests/hardening/password_dictcheck_enabled.sh new file mode 100644 index 0000000..e0cb9ce --- /dev/null +++ b/tests/hardening/password_dictcheck_enabled.sh @@ -0,0 +1,28 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + # prepare to fail + describe Prepare on purpose failed test + apt-get install -y libpam-pwquality + sed -E -i '/^[[:space:]]?dictcheck/d' /etc/security/pwquality.conf + echo "pam_pwquality.so dictcheck=0" >/usr/share/pam-configs/test_cis.conf + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe correcting situation + sed -i 's/audit/enabled/' "${CIS_CONF_DIR}/conf.d/${script}.cfg" + "${CIS_CHECKS_DIR}/${script}.sh" --apply || true + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe clean test + rm -f /usr/share/pam-configs/test_cis.conf + apt-get remove -y libpam-pwquality + +} diff --git a/tests/hardening/password_number_changed_chars.sh b/tests/hardening/password_number_changed_chars.sh new file mode 100644 index 0000000..c301aec --- /dev/null +++ b/tests/hardening/password_number_changed_chars.sh @@ -0,0 +1,28 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + # prepare to fail + describe Prepare on purpose failed test + apt-get install -y libpam-pwquality + sed -E -i '/^[[:space:]]?difok/d' /etc/security/pwquality.conf + echo "pam_pwquality.so difok=1" >/usr/share/pam-configs/test_cis.conf + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe correcting situation + sed -i 's/audit/enabled/' "${CIS_CONF_DIR}/conf.d/${script}.cfg" + "${CIS_CHECKS_DIR}/${script}.sh" --apply || true + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe clean test + rm -f /usr/share/pam-configs/test_cis.conf + apt-get remove -y libpam-pwquality + +} diff --git a/tests/hardening/password_quality_enforced.sh b/tests/hardening/password_quality_enforced.sh new file mode 100644 index 0000000..715073c --- /dev/null +++ b/tests/hardening/password_quality_enforced.sh @@ -0,0 +1,29 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + # prepare to fail + describe Prepare on purpose failed test + apt-get install -y libpam-pwquality + sed -E -i '/^[[:space:]]?enforcing/d' /etc/security/pwquality.conf + echo "enforcing = 0" >>/etc/security/pwquality.conf + echo "pam_pwquality.so enforcing=0" >/usr/share/pam-configs/test_cis.conf + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe correcting situation + sed -i 's/audit/enabled/' "${CIS_CONF_DIR}/conf.d/${script}.cfg" + "${CIS_CHECKS_DIR}/${script}.sh" --apply || true + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe clean test + rm -f /usr/share/pam-configs/test_cis.conf + apt-get remove -y libpam-pwquality + +} diff --git a/tests/hardening/password_quality_enforced_for_root.sh b/tests/hardening/password_quality_enforced_for_root.sh new file mode 100644 index 0000000..e8912dc --- /dev/null +++ b/tests/hardening/password_quality_enforced_for_root.sh @@ -0,0 +1,26 @@ +# shellcheck shell=bash +# run-shellcheck +test_audit() { + + # prepare to fail + describe Prepare on purpose failed test + apt-get install -y libpam-pwquality + sed -E -i '/^[[:space:]]?enforce_for_root/d' /etc/security/pwquality.conf + + describe Running on purpose failed test + register_test retvalshouldbe 1 + # shellcheck disable=2154 + run blank "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe correcting situation + sed -i 's/audit/enabled/' "${CIS_CONF_DIR}/conf.d/${script}.cfg" + "${CIS_CHECKS_DIR}/${script}.sh" --apply || true + + describe Checking resolved state + register_test retvalshouldbe 0 + run resolved "${CIS_CHECKS_DIR}/${script}.sh" --audit-all + + describe clean test + apt-get remove -y libpam-pwquality + +}