lynis/plugins/plugin_pam_phase1

326 lines
19 KiB
Plaintext
Raw Normal View History

#!/bin/sh
#########################################################################
#
# * DO NOT REMOVE *
#-----------------------------------------------------
# PLUGIN_AUTHOR=Michael Boelen <michael.boelen@cisofy.com>
# 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