#!/bin/sh ######################################################################### # # * DO NOT REMOVE * #----------------------------------------------------- # PLUGIN_AUTHOR=Michael Boelen # PLUGIN_CATEGORY=authentication # PLUGIN_DATE=2015-10-01 # PLUGIN_DESC=PAM # PLUGIN_NAME=pam # PLUGIN_PACKAGE=all # PLUGIN_REQUIRED_TESTS= # PLUGIN_VERSION=1.0.0 #----------------------------------------------------- ######################################################################### # PAM_DIRECTORY="/etc/pam.d" # Test : PLGN-0010 # Description : Check PAM configuration if [ -f /etc/pam.conf -o -d /etc/pam.d ]; then PREQS_MET="YES"; else PREQS_MET="NO"; fi Register --test-no PLGN-0010 --preqs-met ${PREQS_MET} --weight L --network NO --description "Check PAM configuration" --progress if [ ${SKIPTEST} -eq 0 ]; then FOUNDPROBLEM=0 # Check if the PAM directory structure exists if [ -d ${PAM_DIRECTORY} ]; then logtext "Result: /etc/pam.d exists" FIND_FILES=`find ${PAM_DIRECTORY} -type f -print` # First check /etc/pam.conf if it exists. #if [ -f /etc/pam.conf ]; then FIND="/etc/pam.conf ${FIND}"; fi for PAM_FILE in ${FIND_FILES}; do #echo "" logtext "Now checking PAM file ${PAM_FILE}" while read line; do # Strip empty lines, commented lines, tabs, line breaks (\), then finally remove all double spaces LINE=`echo $line | grep -v "^#" | grep -v "^$" | tr '\011' ' ' | sed 's/\\\n/ /' | sed 's/ / /g'` if [ ! "${LINE}" = "" ]; then PAM_SERVICE=`echo ${PAM_FILE} | awk -F/ '{ print $NF }'` PAM_CONTROL_FLAG="-" PAM_CONTROL_OPTIONS="-" PAM_MODULE="-" PAM_MODULE_OPTIONS="-" PAM_TYPE=`echo ${LINE} | awk '{ print $1 }'` PARSELINE=0 case ${PAM_TYPE} in "@include") FILE=`echo ${LINE} | awk '{ print $2 }'` #echo "Found @include. Including PAM configuration from file ${FILE}" ;; "account") PARSELINE=1 ;; "auth") PARSELINE=1 ;; "password") PARSELINE=1 ;; "session") PARSELINE=1 ;; *) logtext "Exception: Unknown PAM type found" ;; esac if [ ${PARSELINE} -eq 1 ]; then MULTIPLE_OPTIONS=`echo ${LINE} | awk '$2 ~ /^\[/'` if [ ! "${MULTIPLE_OPTIONS}" = "" ]; then # Needs more parsing, depending on the options found logtext "Result: Found brackets, indicating multiple options for control flags" PAM_CONTROL_OPTIONS=`echo ${LINE} | sed "s/^.*\[//" | sed "s/\].*$//"` else PAM_CONTROL_FLAG=`echo ${LINE} | awk '{ print $2 }'` case ${PAM_CONTROL_FLAG} in "optional"|"required"|"requisite"|"sufficient") logtext "Result: Found known control flag: ${PAM_CONTROL_FLAG}" PAM_MODULE=`echo ${LINE} | awk '{ print $3 }'` PAM_MODULE_OPTIONS=`echo ${LINE} | cut -d ' ' -f 4-` if [ ! "${PAM_MODULE_OPTIONS}" = "" ]; then logtext "Result: using module ${PAM_MODULE} (${PAM_CONTROL_FLAG}) with options ${PAM_MODULE_OPTIONS}" else PAM_MODULE_OPTIONS="-" logtext "Result: using module ${PAM_MODULE} (${PAM_CONTROL_FLAG}) without options configured" fi ;; *) logtext "Unknown control flag found (${PAM_CONTROL_FLAG})" ;; esac fi PAM_MODULE_NAME=`echo ${PAM_MODULE} | sed 's/.so$//'` # # Specific PAMs are commonly seen on these platforms: # # FreeBSD Linux # pam_access v # pam_deny v v # pam_group v # pam_krb5 v # pam_lastlog v # pam_login_access v # pam_nologin v # pam_opie v # pam_opieaccess v # pam_passwdqc v # pam_permit v # pam_rhosts v # pam_rootok v # pam_securetty v # pam_self v # pam_ssh v # pam_unix v case ${PAM_MODULE_NAME} in pam_access) ;; pam_cap) ;; pam_debug | pam_deny) ;; pam_echo| pam_env | pam_exec | pam_faildelay) ;; pam_filter | pam_ftp) ;; # Google Authenticator / YubiKey # Common to find it only enabled for SSH pam_google_authenticator | pam_yubico) logtext "Result: found pam_google_authenticator" if [ "${PAM_CONTROL_FLAG}" = "required" ]; then PAM_2F_AUTH_ENABLED=1 PAM_2F_AUTH_REQUIRED=1 report "authentication_2f_provider[]=${PAM_MODULE_NAME}" report "authentication_2f_service[]=${PAM_SERVICE}" elif -o "${PAM_CONTROL_FLAG}" = "sufficient" ]; then PAM_2F_AUTH_ENABLED=1 report "authentication_2f_provider[]=${PAM_MODULE_NAME}" report "authentication_2f_service[]=${PAM_SERVICE}" else logtext "exception: found 2F authenticator enabled with uncommon control flag: ${PAM_CONTROL_FLAG}" fi ;; pam_group) ;; pam_issue) ;; pam_keyinit | pam_krb5) ;; pam_lastlog | pam_limits) ;; # Log UID for auditd pam_loginuid) PAM_LOGINUID_FOUND=1 ;; pam_listfile | pam_localuser) ;; pam_mail | pam_mkhomedir | pam_motd) ;; pam_namespace | pam_nologin) ;; pam_permit) ;; pam_rootok) ;; pam_rhosts) ;; pam_securetty) ;; pam_self) ;; pam_shells) ;; pam_stress | pam_succeed_if | pam_systemd) ;; pam_time | pam_timestamp) ;; pam_umask) ;; # Password history # Can be configured via pam_unix or pam_pwhistory pam_unix | pam_pwhistory) logtext "Result: found ${PAM_MODULE} module (generic)" if [ ! "${PAM_MODULE_OPTIONS}" = "" ]; then for I in ${PAM_MODULE_OPTIONS}; do OPTION=`echo ${PAM_FILE} | awk -F= '{ print $1 }'` VALUE=`echo ${PAM_FILE} | awk -F= '{ print $2 }'` CREDITS_CONFIGURED=0 case ${OPTION} in # pam_pwhistory / pam_unix remember) # Minimum length (remove 1 if credits are configured, at later stage in function) logtext "Result: password history configured" DigitsOnly ${VALUE} PAM_PASSWORD_HISTORY_AMOUNT=${VALUE} PAM_PASSWORD_HISTORY_ENABLED=1 Debug "Found password history enabled with module ${PAM_MODULE_NAME} and password amount ${PAM_PASSWORD_HISTORY_AMOUNT}" ;; esac done fi ;; pam_unix_acct| pam_unix_auth | pam_unix_passwd | pam_unix_session | pam_unix2) ;; pam_vbox) ;; pam_warn | pam_wheel) ;; pam_xauth) ;; # Password strength testing pam_cracklib | pam_pwquality) logtext "Result: found module ${PAM_MODULE} for password strength testing" PAM_MODULE_PASSWORD_STRENGTH_TESTED=1 if [ ! "${PAM_MODULE_OPTIONS}" = "" ]; then for I in ${PAM_MODULE_OPTIONS}; do OPTION=`echo ${PAM_FILE} | awk -F= '{ print $1 }'` VALUE=`echo ${PAM_FILE} | awk -F= '{ print $2 }'` CREDITS_CONFIGURED=0 case ${OPTION} in minlen) # Minimum length (remove 1 if credits are configured, at later stage in function) logtext "Result: minlen configured" DigitsOnly ${VALUE} MIN_PASSWORD_LENGTH=${VALUE} ;; dccredit) # Digits only DigitsOnly ${VALUE} # Digital characters if [ ${VALUE} -gt 0 ]; then CREDITS_CONFIGURED=1; fi ;; lccredit) # Digits only DigitsOnly ${VALUE} # Lowercase characters if [ ${VALUE} -gt 0 ]; then CREDITS_CONFIGURED=1; fi ;; occredit) # Digits only DigitsOnly ${VALUE} # Other characters if [ ${VALUE} -gt 0 ]; then CREDITS_CONFIGURED=1; fi ;; uccredit) # Digits only DigitsOnly ${VALUE} # Uppercase characters if [ ${VALUE} -gt 0 ]; then CREDITS_CONFIGURED=1; fi ;; esac if [ ${CREDITS_CONFIGURED} -eq 1 ]; then logtext "Result: Credits are configured, password length minus 1" MIN_PASSWORD_LENGTH=`expr ${MIN_PASSWORD_LENGTH} - 1` fi done fi ;; pam_tally | pam_tally2) if [ "${PAM_CONTROL_FLAG}" = "required" ]; then logtext "Result: found a required module for countering brute force cracking attempts" report "pam_auth_brute_force_protection_module[]=${PAM_MODULE_NAME}" PAM_AUTH_BRUTE_FORCE_PROTECTION=1 fi if [ ! "${PAM_MODULE_OPTIONS}" = "" ]; then for I in ${PAM_MODULE_OPTIONS}; do OPTION=`echo ${PAM_FILE} | awk -F= '{ print $1 }'` VALUE=`echo ${PAM_FILE} | awk -F= '{ print $2 }'` case ${OPTION} in deny) AUTH_BLOCK_BAD_LOGIN_ATTEMPTS="${VALUE}" ;; unlock_time) AUTH_UNLOCK_TIME="${VALUE}" ;; esac done fi ;; *) logtext "Result: found pluggable authentication module ${PAM_MODULE}, which is unknown" ;; esac fi Debug "Service: ${PAM_SERVICE}" Debug "Type: ${PAM_TYPE}" Debug "Control: ${PAM_CONTROL_FLAG}" Debug "Control options: ${PAM_CONTROL_OPTIONS}" Debug "Module: ${PAM_MODULE_NAME}" Debug "Module options: ${PAM_MODULE_OPTIONS}" fi done < ${PAM_FILE} #ParsePAMLine ${J} #StoreSetting "pam" " done fi fi # ################################################################################# # # /etc/security/opasswd should exist when: # password history is enabled via pam_unix # pam_cracklib or pam_pwquality is used # In that case, the file should be owned by root, with 440/640/660 permissions logtext "[PAM] PAM 2F authentication enabled: ${PAM_2F_AUTH_ENABLED}" report "authentication_two_factor_enabled=${PAM_2F_AUTH_ENABLED}" logtext "[PAM] PAM 2F authentication required: ${PAM_2F_AUTH_REQUIRED}" report "authentication_two_factor_required=${PAM_2F_AUTH_ENABLED}" if [ ! "${AUTH_UNLOCK_TIME}" = "-1" ]; then logtext "[PAM] Authentication unlock time: ${AUTH_UNLOCK_TIME}" report "authentication_unlock_time=${AUTH_UNLOCK_TIME}" else logtext "[PAM] Authentication unlock time: not configured" fi logtext "[PAM] Password strength testing enabled: ${PAM_PASSWORD_STRENGTH_TESTED}" if [ ${PAM_PASSWORD_STRENGTH_TESTED} -eq 1 ]; then report "password_strength_tested=1" fi logtext "[PAM] Password brute force protection: ${PAM_AUTH_BRUTE_FORCE_PROTECTION}" if [ ${PAM_AUTH_BRUTE_FORCE_PROTECTION} -eq 1 ]; then report "authentication_brute_force_protection=1" fi if [ ! "${MIN_PASSWORD_LENGTH}" = "-1" ]; then logtext "[PAM] Minimum password length: ${MIN_PASSWORD_LENGTH}" report "minimum_password_length=${MIN_PASSWORD_LENGTH}" else logtext "[PAM] Minimum password length: not configured" fi # If auditd is running, but pam_loginuid not, events might not be properly logged if [ ${AUDITD_RUNNING} -eq 1 ]; then if [ ${PAM_LOGINUID_FOUND} -eq 0 ]; then report "pam_issue[]=pam_loginuid is missing" fi fi logtext "[PAM] Password history enabled: ${PAM_PASSWORD_HISTORY_ENABLED}" logtext "[PAM] Password history amount: ${PAM_PASSWORD_HISTORY_AMOUNT}" #EOF