Walkthrough in hardening centos
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1638 lines
59 KiB

#!/bin/bash
#This script will run through several checks and for each check output to the terminal 'OK' or 'ERROR
#The checks are designed to test whether or not the host conforms to the benchmarks in the
#following document
#https://benchmarks.cisecurity.org/tools2/linux/CIS_CentOS_Linux_7_Benchmark_v1.1.0.pdf
#This is aimed to be a starting point for a sysadmin to check or audit hosts he/she supports
#It's envisaged that it will need customising to suit a particular environment
#eg. there are about 200 checks, someone may want to chop out X of them to suit their environment
#The script does not change anything on the host, mostly it runs a lot of greps & cuts
#on config files.
#To quickly get an idea of what this script does have a look at the 'main' and 'func_wrapper' functions
#Copyright (c) 2015, Ross Hamilton. All rights reserved.
# colors
RED_COLOR=`tput setaf 1`
GREEN_COLOR=`tput setaf 2`
RESET_COLOR=`tput sgr0`
FSTAB='/etc/fstab'
YUM_CONF='/etc/yum.conf'
GRUB_CFG='/boot/grub2/grub.cfg'
GRUB_DIR='/etc/grub.d'
SELINUX_CFG='/etc/selinux/config'
NTP_CONF='/etc/ntp.conf'
SYSCON_NTPD='/etc/sysconfig/ntpd'
LIMITS_CNF='/etc/security/limits.conf'
SYSCTL_CNF='/etc/sysctl.conf'
CENTOS_REL='/etc/centos-release'
LATEST_REL_STR='CentOS Linux release 7.6.1810 (Core)'
HOSTS_ALLOW='/etc/hosts.allow'
HOSTS_DENY='/etc/hosts.deny'
CIS_CNF='/etc/modprobe.d/CIS.conf'
RSYSLOG_CNF='/etc/rsyslog.conf'
AUDITD_CNF='/etc/audit/auditd.conf'
AUDIT_RULES='/etc/audit/audit.rules'
LOGR_SYSLOG='/etc/logrotate.d/syslog'
ANACRONTAB='/etc/anacrontab'
CRONTAB='/etc/crontab'
CRON_HOURLY='/etc/cron.hourly'
CRON_DAILY='/etc/cron.daily'
CRON_WEEKLY='/etc/cron.weekly'
CRON_MONTHLY='/etc/cron.monthly'
CRON_DIR='/etc/cron.d'
AT_ALLOW='/etc/at.allow'
AT_DENY='/etc/at.deny'
CRON_ALLOW='/etc/cron.allow'
CRON_DENY='/etc/cron.deny'
SSHD_CFG='/etc/ssh/sshd_config'
SYSTEM_AUTH='/etc/pam.d/system-auth'
PWQUAL_CNF='/etc/security/pwquality.conf'
PASS_AUTH='/etc/pam.d/password-auth'
PAM_SU='/etc/pam.d/su'
GROUP='/etc/group'
LOGIN_DEFS='/etc/login.defs'
PASSWD='/etc/passwd'
SHADOW='/etc/shadow'
GSHADOW='/etc/gshadow'
BASHRC='/etc/bashrc'
PROF_D='/etc/profile.d'
MOTD='/etc/motd'
ISSUE='/etc/issue'
ISSUE_NET='/etc/issue.net'
BANNER_MSG='/etc/dconf/db/gdm.d/01-banner-message'
function separate_partition {
# Test that the supplied $1 is a separate partition
local filesystem="${1}"
grep -q "[[:space:]]${filesystem}[[:space:]]" "${FSTAB}" || return
}
function mount_option {
# Test the the supplied mount option $2 is in use on the supplied filesystem $1
local filesystem="${1}"
local mnt_option="${2}"
grep "[[:space:]]${filesystem}[[:space:]]" "${FSTAB}" | grep -q "${mnt_option}" || return
mount | grep "[[:space:]]${filesystem}[[:space:]]" | grep -q "${mnt_option}" || return
}
function bind_mounted_to {
# Test that a directory /foo/dir is bind mounted onto a particular filesystem
local directory="${1}"
local filesystem="${2}"
local E_NO_MOUNT_OUTPUT=1
grep "^${filesystem}[[:space:]]" "${FSTAB}" | grep -q "${directory}" || return
local grep_mount
grep_mount=$(mount | grep "^${filesystem}[[:space:]]" | grep "${directory}")
#If $directory doesn't appear in the mount output as mounted on the $filesystem
#it may appear in the output as being mounted on the same device as $filesystem, check for this
local fs_dev
local dir_dev
fs_dev="$(mount | grep "[[:space:]]${filesystem}[[:space:]]" | cut -d" " -f1)"
dir_dev="$(mount | grep "[[:space:]]${directory}[[:space:]]" | cut -d" " -f1)"
if [[ -z "${grep_mount}" ]] && [[ "${fs_dev}" != "${dir_dev}" ]] ; then
return "${E_NO_MOUNT_OUTPUT}"
fi
}
function test_disable_mounting {
# Test the the supplied filesystem type $1 is disabled
local module="${1}"
modprobe -n -v ${module} | grep -q "install \+/bin/true" || return
lsmod | grep -qv "${module}" || return
}
function centos_gpg_key_installed {
# Test CentOS GPG Key is installed
local centos_off_str='gpg(CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>)'
rpm -q --queryformat "%{SUMMARY}\n" gpg-pubkey | grep -q "${centos_off_str}" || return
}
function yum_gpgcheck {
# Check that gpgcheck is Globally Activated
cut -d \# -f1 ${YUM_CONF} | grep 'gpgcheck' | grep -q 'gpgcheck=1' || return
}
function yum_update {
# Check for outstanding pkg update with yum
yum -q check-update || return
}
function pkg_integrity {
# Verify the installed packages by comparing the installed files against file info stored in the pkg
local rpm_out
rpm_out="$(rpm -qVa | awk '$2 != "c" { print $0}')"
[[ -z "${rpm_out}" ]] || return
}
function rpm_installed {
# Test whether an rpm is installed
local rpm="${1}"
local rpm_out
rpm_out="$(rpm -q --queryformat "%{NAME}\n" ${rpm})"
[[ "${rpm}" = "${rpm_out}" ]] || return
}
function verify_aide_cron {
# Verify there is a cron job scheduled to run the aide check
crontab -u root -l | cut -d\# -f1 | grep -q "aide \+--check" || return
}
function verify_selinux_grubcfg {
# Verify SELinux is not disabled in grub.cfg file
local grep_out1
grep_out1="$(grep selinux=0 ${GRUB_CFG})"
[[ -z "${grep_out1}" ]] || return
local grep_out2
grep_out2="$(grep enforcing=0 ${GRUB_CFG})"
[[ -z "${grep_out2}" ]] || return
}
function verify_selinux_state {
# Verify SELinux configured state in /etc/selinux/config
cut -d \# -f1 ${SELINUX_CFG} | grep 'SELINUX=' | tr -d '[[:space:]]' | grep -q 'SELINUX=enforcing' || return
}
function verify_selinux_policy {
# Verify SELinux policy in /etc/selinux/config
cut -d \# -f1 ${SELINUX_CFG} | grep 'SELINUXTYPE=' | tr -d '[[:space:]]' | grep -q 'SELINUXTYPE=targeted' || return
}
function rpm_not_installed {
# Check that the supplied rpm $1 is not installed
local rpm="${1}"
rpm -q ${rpm} | grep -q "package ${rpm} is not installed" || return
}
function unconfined_procs {
# Test for unconfined daemons
local ps_out
ps_out="$(ps -eZ | egrep 'initrc|unconfined' | egrep -v 'bash|ps|grep')"
[[ -n "${ps_out}" ]] || return
}
function check_grub_owns {
# Check User/Group Owner on grub.cfg file
stat -L -c "%u %g" ${GRUB_CFG} | grep -q '0 0' || return
}
function check_grub_perms {
# Check Perms on grub.cfg file
stat -L -c "%a" ${GRUB_CFG} | grep -q '.00' || return
}
function check_file_perms {
# Check Perms on a supplied file match supplied pattern
local file="${1}"
local pattern="${2}"
stat -L -c "%a" ${file} | grep -q "${pattern}" || return
}
function check_root_owns {
# Check User/Group Owner on the specified file
local file="${1}"
stat -L -c "%u %g" ${file} | grep -q '0 0' || return
}
function check_boot_pass {
grep -q 'set superusers=' "${GRUB_CFG}"
if [[ "$?" -ne 0 ]]; then
grep -q 'set superusers=' ${GRUB_DIR}/* || return
file="$(grep 'set superusers' ${GRUB_DIR}/* | cut -d: -f1)"
grep -q 'password' "${file}" || return
else
grep -q 'password' "${GRUB_CFG}" || return
fi
}
function check_svc_not_enabled {
# Verify that the service $1 is not enabled
local service="$1"
systemctl list-unit-files | grep -qv "${service}" && return
systemctl is-enabled "${service}" | grep -q 'enabled' || return
}
function check_svc_enabled {
# Verify that the service $1 is enabled
local service="$1"
systemctl list-unit-files | grep -q "${service}.service" || return
systemctl is-enabled "${service}" | grep -q 'enabled' && return
}
function ntp_cfg {
cut -d\# -f1 ${NTP_CONF} | egrep "restrict{1}[[:space:]]+default{1}" ${NTP_CONF} | grep kod \
| grep nomodify | grep notrap | grep nopeer | grep -q noquery || return
cut -d\# -f1 ${NTP_CONF} | egrep "restrict{1}[[:space:]]+\-6{1}[[:space:]]+default" | grep kod \
| grep nomodify | grep notrap | grep nopeer | grep -q noquery || return
cut -d\# -f1 ${NTP_CONF} | egrep -q "^[[:space:]]*server" || return
cut -d\# -f1 ${SYSCON_NTPD} | grep "OPTIONS=" | grep -q "ntp:ntp" || return
}
function restrict_core_dumps {
# Verify that suid programs cannot dump their core
egrep -q "\*{1}[[:space:]]+hard[[:space:]]+core[[:space:]]+0" "${LIMITS_CNF}" || return
cut -d\# -f1 ${SYSCTL_CNF} | grep fs.suid_dumpable | cut -d= -f2 | tr -d '[[:space:]]' | grep -q '0' || return
}
function chk_sysctl_cnf {
# Check the sysctl_conf file contains a particular flag, set to a particular value
local flag="$1"
local value="$2"
local sysctl_cnf="$3"
cut -d\# -f1 ${sysctl_cnf} | grep "${flag}" | cut -d= -f2 | tr -d '[[:space:]]' | grep -q "${value}" || return
}
function chk_sysctl {
local flag="$1"
local value="$2"
sysctl "${flag}" | cut -d= -f2 | tr -d '[[:space:]]' | grep -q "${value}" || return
}
function chk_latest_rel {
grep -q "${LATEST_REL_STR}" "${CENTOS_REL}" || return
}
function sticky_wrld_w_dirs {
dirs="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d \
\( -perm -0002 -a ! -perm -1000 \))"
[[ -z "${dirs}" ]] || return
}
function check_umask {
cut -d\# -f1 /etc/sysconfig/init | grep -q "umask[[:space:]]027" || return
}
function check_def_tgt {
#Check that the default boot target is multi-user.target
local default_tgt
default_tgt="$(systemctl get-default)"
[[ "${default_tgt}" = "multi-user.target" ]] || return
}
function mta_local_only {
# If port 25 is being listened on, check it is on the loopback address
netstat_out="$(netstat -an | grep "LIST" | grep ":25[[:space:]]")"
if [[ "$?" -eq 0 ]] ; then
ip=$(echo ${netstat_out} | cut -d: -f1 | cut -d" " -f4)
[[ "${ip}" = "127.0.0.1" ]] || return
fi
}
function ip6_router_advertisements_dis {
# Check that IPv6 Router Advertisements are disabled
# If ipv6 is disabled then we don't mind what IPv6 router advertisements are set to
# If ipv6 is enabled then both settings should be set to zero
chk_sysctl net.ipv6.conf.all.disable_ipv6 1 && return
chk_sysctl net.ipv6.conf.all.accept_ra 0 || return
chk_sysctl net.ipv6.conf.default.accept_ra 0 || return
}
function ip6_redirect_accept_dis {
# Check that IPv6 Redirect Acceptance is disabled
# If ipv6 is disabled then we don't mind what IPv6 redirect acceptance is set to
# If ipv6 is enabled then both settings should be set to zero
chk_sysctl net.ipv6.conf.all.disable_ipv6 1 && return
chk_sysctl net.ipv6.conf.all.accept_redirects 0 || return
chk_sysctl net.ipv6.conf.default.accept_redirects 0 || return
}
function chk_file_exists {
local file="$1"
[[ -f "${file}" ]] || return
}
function chk_hosts_deny_content {
# Check the hosts.deny file resembles ALL: ALL
cut -d\# -f1 ${HOSTS_DENY} | grep -q "ALL[[:space:]]*:[[:space:]]*ALL" || return
}
function chk_cis_cnf {
local protocol="$1"
local file="$2"
grep -q "install[[:space:]]${protocol}[[:space:]]/bin/true" ${file} || return
}
function chk_rsyslog_content {
# rsyslog should be configured to send logs to a remote host
# grep output should resemble
# *.* @@loghost.example.com
grep -q "^*.*[^I][^I]*@" ${RSYSLOG_CNF} || return
}
function audit_log_storage_size {
# Check the max size of the audit log file is configured
cut -d\# -f1 ${AUDITD_CNF} | egrep -q "max_log_file[[:space:]]|max_log_file=" || return
}
function dis_on_audit_log_full {
# Check auditd.conf is configured to notify the admin and halt the system when audit logs are full
cut -d\# -f2 ${AUDITD_CNF} | grep 'space_left_action' | cut -d= -f2 | tr -d '[[:space:]]' | grep -q 'email' || return
cut -d\# -f2 ${AUDITD_CNF} | grep 'action_mail_acct' | cut -d= -f2 | tr -d '[[:space:]]' | grep -q 'root' || return
cut -d\# -f2 ${AUDITD_CNF} | grep 'admin_space_left_action' | cut -d= -f2 | tr -d '[[:space:]]' | grep -q 'halt' || return
}
function keep_all_audit_info {
# Check auditd.conf is configured to retain audit logs
cut -d\# -f2 ${AUDITD_CNF} | grep 'max_log_file_action' | cut -d= -f2 | tr -d '[[:space:]]' | grep -q 'keep_logs' || return
}
function audit_procs_prior_2_auditd {
# Check lines that start with linux have the audit=1 parameter set
grep_grub="$(grep "^[[:space:]]*linux" ${GRUB_CFG} | grep -v 'audit=1')"
[[ -z "${grep_grub}" ]] || return
}
function audit_date_time {
# Confirm that the time-change lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+time-change" | egrep "\-S[[:space:]]+settimeofday" \
| egrep "\-S[[:space:]]+adjtimex" | egrep "\-F[[:space:]]+arch=b64" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+time-change" | egrep "\-S[[:space:]]+settimeofday" \
| egrep "\-S[[:space:]]+adjtimex" | egrep "\-F[[:space:]]+arch=b32" | egrep "\-S[[:space:]]+stime" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+time-change" | egrep "\-F[[:space:]]+arch=b64" \
| egrep "\-S[[:space:]]+clock_settime" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+time-change" | egrep "\-F[[:space:]]+arch=b32" \
| egrep "\-S[[:space:]]+clock_settime" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+time-change" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/localtime" || return
}
function audit_user_group {
# Confirm that the identity lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+identity" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/group" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+identity" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/passwd" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+identity" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/gshadow" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+identity" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/shadow" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+identity" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/security\/opasswd" || return
}
function audit_network_env {
# Confirm that the system-locale lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-S[[:space:]]+sethostname" \
| egrep "\-S[[:space:]]+setdomainname" | egrep "\-F[[:space:]]+arch=b64" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-S[[:space:]]+sethostname" \
| egrep "\-S[[:space:]]+setdomainname" | egrep "\-F[[:space:]]+arch=b32" | egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/issue" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/issue.net" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/hosts" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+system-locale" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/sysconfig\/network" || return
}
function audit_logins_logouts {
# Confirm that the logins lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+logins" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/faillog" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+logins" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/lastlog" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+logins" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/tallylog" || return
}
function audit_session_init {
# Confirm that the logins lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+session" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/run\/utmp" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+session" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/wtmp" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+session" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/btmp" || return
}
function audit_sys_mac {
# Confirm that the logins lines specified below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+MAC-policy" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/selinux\/" || return
}
function audit_dac_perm_mod_events {
# Confirm that perm_mod lines matching the patterns below do appear in the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+chmod" \
| egrep "\-S[[:space:]]+fchmod" | egrep "\-S[[:space:]]+fchmodat" | egrep "\-F[[:space:]]+arch=b64" \
| egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+chmod" \
| egrep "\-S[[:space:]]+fchmod" | egrep "\-S[[:space:]]+fchmodat" | egrep "\-F[[:space:]]+arch=b32" \
| egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+chown" \
| egrep "\-S[[:space:]]+fchown" | egrep "\-S[[:space:]]+fchownat" | egrep "\-S[[:space:]]+fchown" \
| egrep "\-F[[:space:]]+arch=b64" | egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+chown" \
| egrep "\-S[[:space:]]+fchown" | egrep "\-S[[:space:]]+fchownat" | egrep "\-S[[:space:]]+fchown" \
| egrep "\-F[[:space:]]+arch=b32" | egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+setxattr" \
| egrep "\-S[[:space:]]+lsetxattr" | egrep "\-S[[:space:]]+fsetxattr" | egrep "\-S[[:space:]]+removexattr" \
| egrep "\-S[[:space:]]+lremovexattr" | egrep "\-S[[:space:]]+fremovexattr" | egrep "\-F[[:space:]]+arch=b64" \
| egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+perm_mod" | egrep "\-S[[:space:]]+setxattr" \
| egrep "\-S[[:space:]]+lsetxattr" | egrep "\-S[[:space:]]+fsetxattr" | egrep "\-S[[:space:]]+removexattr" \
| egrep "\-S[[:space:]]+lremovexattr" | egrep "\-S[[:space:]]+fremovexattr" | egrep "\-F[[:space:]]+arch=b32" \
| egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
}
function unsuc_unauth_acc_attempts {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+access" | egrep "\-S[[:space:]]+creat" \
| egrep "\-S[[:space:]]+open" | egrep "\-S[[:space:]]+openat" | egrep "\-S[[:space:]]+truncate" \
| egrep "\-S[[:space:]]+ftruncate" | egrep "\-F[[:space:]]+arch=b64" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" | egrep "\-F[[:space:]]exit=\-EACCES" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+access" | egrep "\-S[[:space:]]+creat" \
| egrep "\-S[[:space:]]+open" | egrep "\-S[[:space:]]+openat" | egrep "\-S[[:space:]]+truncate" \
| egrep "\-S[[:space:]]+ftruncate" | egrep "\-F[[:space:]]+arch=b32" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" | egrep "\-F[[:space:]]exit=\-EACCES" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+access" | egrep "\-S[[:space:]]+creat" \
| egrep "\-S[[:space:]]+open" | egrep "\-S[[:space:]]+openat" | egrep "\-S[[:space:]]+truncate" \
| egrep "\-S[[:space:]]+ftruncate" | egrep "\-F[[:space:]]+arch=b64" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" | egrep "\-F[[:space:]]exit=\-EPERM" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+access" | egrep "\-S[[:space:]]+creat" \
| egrep "\-S[[:space:]]+open" | egrep "\-S[[:space:]]+openat" | egrep "\-S[[:space:]]+truncate" \
| egrep "\-S[[:space:]]+ftruncate" | egrep "\-F[[:space:]]+arch=b32" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" | egrep "\-F[[:space:]]exit=\-EPERM" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
}
function coll_priv_cmds {
local priv_cmds
priv_cmds="$(find / -xdev \( -perm -4000 -o -perm -2000 \) -type f)"
for cmd in ${priv_cmds} ; do
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+privileged" | egrep "\-F[[:space:]]+path=${cmd}" \
| egrep "\-F[[:space:]]+perm=x" | egrep "\-F[[:space:]]+auid>=1000" | egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
done
}
function coll_suc_fs_mnts {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+mounts" | egrep "\-S[[:space:]]+mount" \
| egrep "\-F[[:space:]]+arch=b64" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+mounts" | egrep "\-S[[:space:]]+mount" \
| egrep "\-F[[:space:]]+arch=b32" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
}
function coll_file_del_events {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+delete" | egrep "\-S[[:space:]]+unlink" \
| egrep "\-F[[:space:]]+arch=b64" | egrep "\-S[[:space:]]+unlinkat" | egrep "\-S[[:space:]]+rename" \
| egrep "\-S[[:space:]]+renameat" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+delete" | egrep "\-S[[:space:]]+unlink" \
| egrep "\-F[[:space:]]+arch=b32" | egrep "\-S[[:space:]]+unlinkat" | egrep "\-S[[:space:]]+rename" \
| egrep "\-S[[:space:]]+renameat" | egrep "\-F[[:space:]]+auid>=1000" \
| egrep "\-F[[:space:]]+auid\!=4294967295" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
}
function coll_chg2_sysadm_scope {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+scope" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/etc\/sudoers" || return
}
function coll_sysadm_actions {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+actions" | egrep "\-p[[:space:]]+wa" \
| egrep -q "\-w[[:space:]]+\/var\/log\/sudo.log" || return
}
function kmod_lod_unlod {
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+modules" | egrep "\-p[[:space:]]+x" \
| egrep -q "\-w[[:space:]]+\/sbin\/insmod" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+modules" | egrep "\-p[[:space:]]+x" \
| egrep -q "\-w[[:space:]]+\/sbin\/rmmod" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+modules" | egrep "\-p[[:space:]]+x" \
| egrep -q "\-w[[:space:]]+\/sbin\/modprobe" || return
cut -d\# -f1 ${AUDIT_RULES} | egrep "\-k[[:space:]]+modules" | egrep "\-S[[:space:]]+delete_module" \
| egrep "\-F[[:space:]]+arch=b64" | egrep "\-S[[:space:]]+init_module" \
| egrep -q "\-a[[:space:]]+always,exit|\-a[[:space:]]+exit,always" || return
}
function audit_cfg_immut {
# There should be a "-e 2" at the end of the audit.rules file
cut -d\# -f1 ${AUDIT_RULES} | egrep -q "^-e[[:space:]]+2" || return
}
function logrotate_cfg {
[[ -f "${LOGR_SYSLOG}" ]] || return
local timestamp
timestamp=$(date '+%Y%m%d_%H%M%S')
local tmp_data="/tmp/logrotate.tmp.${timestamp}"
local file_list="/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler /var/log/boot.log /var/log/cron"
local line_num
line_num=$(grep -n '{' "${LOGR_SYSLOG}" | cut -d: -f1)
line_num=$((${line_num} - 1))
head -${line_num} "${LOGR_SYSLOG}" > ${tmp_data}
for file in ${file_list} ; do
grep -q "${file}" ${tmp_data} || return
done
rm "${tmp_data}"
}
function atd_cfg {
[[ ! -f ${AT_DENY} ]] || return
[[ -f ${AT_ALLOW} ]] || return
check_root_owns "${AT_ALLOW}"
check_file_perms "${AT_ALLOW}" 600
}
function at_cron_auth_users {
[[ ! -f ${AT_DENY} ]] || return
[[ ! -f ${CRON_DENY} ]] || return
check_root_owns "${CRON_ALLOW}"
check_root_owns "${AT_ALLOW}"
check_file_perms "${CRON_ALLOW}" 600
check_file_perms "${AT_ALLOW}" 600
}
function chk_param {
local file="${1}"
local parameter="${2}"
local value="${3}"
cut -d\# -f1 ${file} | egrep -q "^${parameter}[[:space:]]+${value}" || return
}
function ssh_maxauthtries {
local allowed_max="${1}"
local actual_value
actual_value=$(cut -d\# -f1 ${SSHD_CFG} | grep 'MaxAuthTries' | cut -d" " -f2)
[[ ${actual_value} -le ${allowed_max} ]] || return
}
function ssh_access {
local allow_users
local allow_groups
local deny_users
local deny_users
allow_users="$(cut -d\# -f1 ${SSHD_CFG} | grep "AllowUsers" | cut -d" " -f2)"
allow_groups="$(cut -d\# -f1 ${SSHD_CFG} | grep "AllowGroups" | cut -d" " -f2)"
deny_users="$(cut -d\# -f1 ${SSHD_CFG} | grep "DenyUsers" | cut -d" " -f2)"
deny_groups="$(cut -d\# -f1 ${SSHD_CFG} | grep "DenyGroups" | cut -d" " -f2)"
[[ -n "${allow_users}" ]] || return
[[ -n "${allow_groups}" ]] || return
[[ -n "${deny_users}" ]] || return
[[ -n "${deny_groups}" ]] || return
}
function pass_hash_algo {
local algo="${1}"
authconfig --test | grep 'hashing' | grep -q "${algo}" || return
}
function pass_req_params {
# verify the pam_pwquality.so params in /etc/pam.d/system-auth
grep pam_pwquality.so ${SYSTEM_AUTH} | grep 'password' | grep 'requisite' | grep 'try_first_pass' | grep 'local_users_only' | grep 'retry=3' | grep -q 'authtok_type=' || return
grep -q 'minlen=14' ${PWQUAL_CNF} || return
grep -q 'dcredit=-1' ${PWQUAL_CNF} || return
grep -q 'ucredit=-1' ${PWQUAL_CNF} || return
grep -q 'ocredit=-1' ${PWQUAL_CNF} || return
grep -q 'lcredit=-1' ${PWQUAL_CNF} || return
}
function failed_pass_lock {
egrep "auth[[:space:]]+required" ${PASS_AUTH} | grep -q 'pam_deny.so' || return
egrep "auth[[:space:]]+required" ${PASS_AUTH} | grep 'pam_faillock.so' | grep 'preauth' | grep 'audit' | grep 'silent' | grep 'deny=5' | grep -q 'unlock_time=900' || return
grep 'auth' ${PASS_AUTH} | grep 'pam_unix.so' | egrep -q "\[success=1[[:space:]]+default=bad\]" || return
grep 'auth' ${PASS_AUTH} | grep 'pam_faillock.so' | grep 'authfail' | grep 'audit' | grep 'deny=5' | grep 'unlock_time=900' | egrep -q "\[default=die\]" || return
egrep "auth[[:space:]]+sufficient" ${PASS_AUTH} | grep 'pam_faillock.so' | grep 'authsucc' | grep 'audit' | grep 'deny=5' | grep -q 'unlock_time=900' || return
egrep "auth[[:space:]]+required" ${PASS_AUTH} | grep -q 'pam_deny.so' || return
egrep "auth[[:space:]]+required" ${SYSTEM_AUTH} | grep -q 'pam_env.so' || return
egrep "auth[[:space:]]+required" ${SYSTEM_AUTH} | grep 'pam_faillock.so' | grep 'preauth' | grep 'audit' | grep 'silent' | grep 'deny=5' | grep -q 'unlock_time=900' || return
grep 'auth' ${SYSTEM_AUTH} | grep 'pam_unix.so' | egrep -q "\[success=1[[:space:]]+default=bad\]" || return
grep 'auth' ${SYSTEM_AUTH} | grep 'pam_faillock.so' | grep 'authfail' | grep 'audit' | grep 'deny=5' | grep 'unlock_time=900' | egrep -q "\[default=die\]" || return
egrep "auth[[:space:]]+sufficient" ${SYSTEM_AUTH} | grep 'pam_faillock.so' | grep 'authsucc' | grep 'audit' | grep 'deny=5' | grep -q 'unlock_time=900' || return
egrep "auth[[:space:]]+required" ${SYSTEM_AUTH} | grep -q 'pam_deny.so' || return
}
function lim_passwd_reuse {
egrep "auth[[:space:]]+sufficient" ${SYSTEM_AUTH} | grep 'pam_unix.so' | grep -q 'remember=5' || return
}
function su_access {
egrep "auth[[:space:]]+required" "${PAM_SU}" | grep 'pam_wheel.so' | grep -q 'use_uid' || return
grep 'wheel' "${GROUP}" | cut -d: -f4 | grep -q 'root' || return
}
function dis_sys_accs {
# Check that system accounts are disabled
local accounts
accounts="$(egrep -v "^\+" /etc/passwd | awk -F: '($1!="root" && $1!="sync" && $1!="shutdown" \
&& $1!="halt" && $3<1000 && $7!="/sbin/nologin") {print}')"
[[ -z "${accounts}" ]] || return
}
function root_def_grp {
local gid1
local gid2
gid1="$(grep "^root:" "${PASSWD}" | cut -d: -f4)"
[[ "${gid1}" -eq 0 ]] || return
gid2="$(id -g root)"
[[ "${gid2}" -eq 0 ]] || return
}
function def_umask_for_users {
cut -d\# -f1 "${BASHRC}" | egrep -q "umask[[:space:]]+027" || return
egrep -q "umask[[:space:]]+027" ${PROF_D}/* || return
}
function inactive_usr_acs_locked {
# After being inactive for a period of time the account should be disabled
local days
local inactive_threshold=30
days="$(useradd -D | grep INACTIVE | cut -d= -f2)"
[[ ${days} -ge ${inactive_threshold} ]] || return
}
function warning_banners {
# Check that system login banners don't contain any OS information
local motd
local issue
local issue_net
motd="$(egrep '(\\v|\\r|\\m|\\s)' ${MOTD})"
issue="$(egrep '(\\v|\\r|\\m|\\s)' ${ISSUE})"
issue_net="$(egrep '(\\v|\\r|\\m|\\s)' ${ISSUE_NET})"
[[ -z "${motd}" ]] || return
[[ -z "${issue}" ]] || return
[[ -z "${issue_net}" ]] || return
}
function gnome_banner {
# On a host aiming to meet CIS requirements GNOME is unlikely to be installed
# Thus the function says if the file exists then it should have these lines in it
if [[ -f "${BANNER_MSG}" ]] ; then
egrep '[org/gnome/login-screen]' ${BANNER_MSG} || return
egrep 'banner-message-enable=true' ${BANNER_MSG} || return
egrep 'banner-message-text=' ${BANNER_MSG} || return
fi
}
function unowned_files {
local uo_files
uo_files="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nouser)"
[[ -z "${uo_files}" ]] || return
}
function ungrouped_files {
local ug_files
ug_files="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nogroup)"
[[ -z "${ug_files}" ]] || return
}
function suid_exes {
# For every suid exe on the host use the rpm cmd to verify that it should be suid executable
# If the rpm cmd returns no output then the rpm is as it was when it was installed so no prob
local suid_exes
suid_exes="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 -print)"
for suid_exe in ${suid_exes}
do
rpm_out="$(rpm -V $(rpm -qf ${suid_exe}))"
[[ -z "${rpm_out}" ]] || return
done
}
function sgid_exes {
# For every sgid exe on the host use the rpm cmd to verify that it should be sgid executable
# If the rpm cmd returns no output then the rpm is as it was when it was installed so no prob
local sgid_exes
sgid_exes="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 -print)"
for sgid_exe in ${sgid_exes}
do
rpm_out="$(rpm -V $(rpm -qf ${sgid_exe}))"
[[ -z "${rpm_out}" ]] || return
done
}
function passwd_field_chk {
local shadow_out
shadow_out="$(awk -F: '($2 == "" ) { print $1 }' ${SHADOW})"
[[ -z "${shadow_out}" ]] || return
}
function nis_in_file {
# Check for lines starting with + in the supplied file $1
# In /etc/{passwd,shadow,group} it used to be a marker to insert data from NIS
# There shouldn't be any entries like this
local file="${1}"
local grep_out
grep_out="$(grep '^+:' ${file})"
[[ -z "${grep_out}" ]] || return
}
function no_uid0_other_root {
local grep_passwd
grep_passwd="$(awk -F: '($3 == 0) { print $1 }' ${PASSWD})"
[[ "${grep_passwd}" = "root" ]] || return
}
function world_w_dirs {
dirs="$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002)"
[[ -z "${dirs}" ]] || return
}
function root_path {
# There should not be an empty dir in $PATH
local grep=/bin/grep
local sed=/bin/sed
path_grep="$(echo ${PATH} | ${grep} '::')"
[[ -z "${path_grep}" ]] || return
# There should not be a trailing : on $PATH
path_grep="$(echo ${PATH} | ${grep} :$)"
[[ -z "${path_grep}" ]] || return
path_dirs="$(echo $PATH | ${sed} -e 's/::/:/' -e 's/:$//' -e 's/:/ /g')"
for dir in ${path_dirs} ; do
# PATH should not contain .
[[ "${dir}" != "." ]] || return
#$dir should be a directory
[[ -d "${dir}" ]] || return
local ls_out
ls_out="$(ls -ldH ${dir})"
if is_group_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_writable ${ls_out} ; then return 1 ; else return 0 ; fi
# Directory should be owned by root
dir_own="$(echo ${ls_out} | awk '{print $3}')"
[[ "${dir_own}" = "root" ]] || return
done
}
function is_group_readable {
local ls_output="${1}"
# 5th byte of ls output is the field for group readable
[[ "${ls_output:4:1}" = "r" ]] || return
}
function is_group_writable {
local ls_output="${1}"
# 6th byte of ls output is the field for group writable
[[ "${ls_output:5:1}" = "w" ]] || return
}
function is_group_executable {
local ls_output="${1}"
# 7th byte of ls output is the field for group readable
[[ "${ls_output:6:1}" = "r" ]] || return
}
function is_other_readable {
local ls_output="${1}"
# 8th byte of ls output is the field for other readable
[[ "${ls_output:7:1}" = "r" ]] || return
}
function is_other_writable {
local ls_output="${1}"
# 9th byte of ls output is the field for other writable
[[ "${ls_output:8:1}" = "w" ]] || return
}
function is_other_executable {
local ls_output="${1}"
# 10th byte of ls output is the field for other executable
[[ "${ls_output:9:1}" = "x" ]] || return
}
function home_dir_perms {
dirs="$(grep -v 'root|halt|sync|shutdown' ${PASSWD} | awk -F: '($7 != "/sbin/nologin") { print $6 }')"
[[ -z "${dirs}" ]] && return
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
local ls_out
ls_out="$(ls -ldH ${dir})"
if is_group_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_readable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_executable ${ls_out} ; then return 1 ; else return 0 ; fi
done
}
function dot_file_perms {
dirs="$(grep -v 'root|halt|sync|shutdown' ${PASSWD} | awk -F: '($7 != "/sbin/nologin") { print $6 }')"
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
for file in ${dir}/.[A-Za-z0-9]* ; do
if [[ ! -h "${file}" && -f "${file}" ]] ; then
local ls_out
ls_out="$(ls -ldH ${dir})"
if is_group_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_writable ${ls_out} ; then return 1 ; else return 0 ; fi
fi
done
done
}
function dot_rhosts_files {
dirs="$(grep -v 'root|halt|sync|shutdown' ${PASSWD} | awk -F: '($7 != "/sbin/nologin") { print $6 }')"
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
local file="${dir}/.rhosts"
if [[ ! -h "${file}" && -f "${file}" ]] ; then
return 1
else
return 0
fi
done
}
function chk_groups_passwd {
# We don't want to see any groups in /etc/passwd that aren't in /etc/group
group_ids="$(cut -s -d: -f4 ${PASSWD} | sort -u)"
for group_id in ${group_ids} ; do
grep -q -P "^.*?:x:${group_id}:" ${GROUP} || return
done
}
function chk_home_dirs_exist {
#Check that users home directory do all exist
while read user uid dir ; do
if [[ "${uid}" -ge 1000 && ! -d "${dir}" && "${user}" != "nfsnobody" ]] ; then
return 1
fi
done < <(awk -F: '{ print $1 " " $3 " " $6 }' ${PASSWD})
}
function chk_home_dirs_owns {
#Check that users home directory do all exist
while read user uid dir ; do
if [[ "${uid}" -ge 1000 && ! -d "${dir}" && "${user}" != "nfsnobody" ]] ; then
local owner
owner="$(stat -L -c "%U" "${dir}")"
[[ "${owner}" = "${user}" ]] || return
fi
done < <(awk -F: '{ print $1 " " $3 " " $6 }' ${PASSWD})
}
function dot_netrc_perms {
dirs="$(grep -v 'root|halt|sync|shutdown' ${PASSWD} | awk -F: '($7 != "/sbin/nologin") { print $6 }')"
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
for file in ${dir}/.netrc ; do
if [[ ! -h "${file}" && -f "${file}" ]] ; then
local ls_out
ls_out="$(ls -ldH ${dir})"
if is_group_readable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_group_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_group_executable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_readable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_writable ${ls_out} ; then return 1 ; else return 0 ; fi
if is_other_executable ${ls_out} ; then return 1 ; else return 0 ; fi
fi
done
done
}
function user_dot_netrc {
# We don't want to see any ~/.netrc files
local dirs
dirs="$(cut -d: -f6 ${PASSWD})"
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
if [[ ! -h "${dir}/.netrc" && -f "${dir}/.netrc" ]] ; then
return 1
fi
done
}
function user_dot_forward {
# We don't want to see any ~/.forward files
local dirs
dirs="$(cut -d: -f6 ${PASSWD})"
for dir in ${dirs} ; do
[[ -d "${dir}" ]] || continue
if [[ ! -h "${dir}/.forward" && -f "${dir}/.forward" ]] ; then
return 1
fi
done
}
function duplicate_uids {
local num_of_uids
local uniq_num_of_uids
num_of_uids="$(cut -f3 -d":" ${PASSWD} | wc -l)"
uniq_num_of_uids="$(cut -f3 -d":" ${PASSWD} | sort -n | uniq | wc -l)"
[[ "${num_of_uids}" -eq "${uniq_num_of_uids}" ]] || return
}
function duplicate_gids {
local num_of_gids
local uniq_num_of_gids
num_of_gids="$(cut -f3 -d":" ${GROUP} | wc -l)"
uniq_num_of_gids="$(cut -f3 -d":" ${GROUP} | sort -n | uniq | wc -l)"
[[ "${num_of_gids}" -eq "${uniq_num_of_gids}" ]] || return
}
function chk_uids_4_res {
local default_users='root bin daemon adm lp sync shutdown halt mail news uucp operator games \
gopher ftp nobody nscd vcsa rpc mailnull smmsp pcap ntp dbus avahi sshd rpcuser \
nfsnobody haldaemon avahi-autoipd distcache apache oprofile webalizer dovecot squid \
named xfs gdm sabayon usbmuxd rtkit abrt saslauth pulse postfix tcpdump polkitd tss chrony'
while read user uid; do
local found=0
for duser in ${default_users} ; do
if [[ "${user}" = "${duser}" ]] ; then
found=1
fi
done
[[ "${found}" -eq 1 ]] || return
done < <(awk -F: '($3 < 1000) {print $1" "$3 }' ${PASSWD})
}
function duplicate_usernames {
local num_of_usernames
local num_of_uniq_usernames
num_of_usernames="$(cut -f1 -d":" ${PASSWD} | wc -l)"
num_of_uniq_usernames="$(cut -f1 -d":" ${PASSWD} | sort | uniq | wc -l)"
[[ "${num_of_usernames}" -eq "${num_of_uniq_usernames}" ]] || return
}
function duplicate_groupnames {
local num_of_groupnames
local num_of_uniq_groupnames
num_of_groupnames="$(cut -f1 -d":" ${GROUP} | wc -l)"
num_of_uniq_groupnames="$(cut -f1 -d":" ${GROUP} | sort | uniq | wc -l)"
[[ "${num_of_groupnames}" -eq "${num_of_uniq_groupnames}" ]] || return
}
function func_wrapper {
func_name=$1
shift
args=$@
${func_name} ${args}
if [[ "$?" -eq 0 ]]; then
echo ${func_name} ${args} ${GREEN_COLOR}OK${RESET_COLOR}
else
echo ${func_name} ${args} ${RED_COLOR}ERROR${RESET_COLOR}
fi
}
function main {
# CIS 1.1.1 Test that there is a separate /tmp partition
func_wrapper separate_partition /tmp
# CIS 1.1.2 Test that the nodev option is on the /tmp partition
func_wrapper mount_option /tmp nodev
# CIS 1.1.3 Test that the nosuid option is on the /tmp partition
func_wrapper mount_option /tmp nosuid
# CIS 1.1.4 Test that the noexec option is on the /tmp partition
func_wrapper mount_option /tmp noexec
# CIS 1.1.5 Test that there is a separate /var partition
func_wrapper separate_partition /var
# CIS 1.1.6 Test that the /var/tmp directory is bind mounted onto the /tmp filesystem
func_wrapper bind_mounted_to /var/tmp /tmp
# CIS 1.1.7 Test that there is a separate /var/log partition
func_wrapper separate_partition /var/log
# CIS 1.1.8 Test that there is a separate /var/log/audit partition
func_wrapper separate_partition /var/log/audit
# CIS 1.1.9 Test that there is a separate /home partition
func_wrapper separate_partition /home
# CIS 1.1.10 Test that the nodev option is on the /home partition
func_wrapper mount_option /home nodev
# TO DO CIS 1.1.11 nodev option on removable media partitions
# TO DO CIS 1.1.12 noexec option on removable media partitions
# TO DO CIS 1.1.13 nosuid option on removable media partitions
# CIS 1.1.14 Test that the nodev option is on the /dev/shm partition
func_wrapper mount_option /dev/shm nodev
# CIS 1.1.15 Test that the nosuid option is on the /dev/shm partition
func_wrapper mount_option /dev/shm nosuid
# CIS 1.1.16 Test that the noexec option is on the /dev/shm partition
func_wrapper mount_option /dev/shm noexec
# TO DO CIS 1.1.17 Check for the sticky bit on all world writable dirs
func_wrapper sticky_wrld_w_dirs
# CIS 1.1.18 Test that the mounting of cramfs filesystems is disabled
func_wrapper test_disable_mounting cramfs
# CIS 1.1.19 Test that the mounting of freevxfs filesystems is disabled
func_wrapper test_disable_mounting freevxfs
# CIS 1.1.20 Test that the mounting of jffs2 filesystems is disabled
func_wrapper test_disable_mounting jffs2
# CIS 1.1.21 Test that the mounting of hfs filesystems is disabled
func_wrapper test_disable_mounting hfs
# CIS 1.1.22 Test that the mounting of hfsplus filesystems is disabled
func_wrapper test_disable_mounting hfsplus
# CIS 1.1.23 Test that the mounting of squashfs filesystems is disabled
func_wrapper test_disable_mounting squashfs
# CIS 1.1.24 Test that the mounting of udf filesystems is disabled
func_wrapper test_disable_mounting udf
# CIS 1.2.1 Check that the CentOS GPG Key is Installed
func_wrapper centos_gpg_key_installed
# CIS 1.2.2 Check that gpgcheck is globally activated
func_wrapper yum_gpgcheck
# CIS 1.2.3 Check software package updates with yum
func_wrapper yum_update
# CIS 1.2.4 Verify package integrity using RPM
func_wrapper pkg_integrity
# CIS 1.3.1 Check that the AIDE rpm is installed
func_wrapper rpm_installed aide
# CIS 1.3.2 Check periodic execution of file integrity (that aide runs from cron)
func_wrapper verify_aide_cron
# CIS 1.4.1 Check that SELinux is not disabled in /boot/grub2/grub.cfg
func_wrapper verify_selinux_grubcfg
# CIS 1.4.2 Verify SELinux configured state in /etc/selinux/config
func_wrapper verify_selinux_state
# CIS 1.4.3 Verify SELinux policy in /etc/selinux/config
func_wrapper verify_selinux_policy
# CIS 1.4.4 Check setroubleshoot RPM is not installed
func_wrapper rpm_not_installed setroubleshoot
# CIS 1.4.5 Check setroubleshoot RPM is not installed
func_wrapper rpm_not_installed mcstrans
# CIS 1.4.6 Check for unconfined daemons
func_wrapper unconfined_procs
# CIS 1.5.1 Check ownership on /boot/grub2/grub.cfg
func_wrapper check_root_owns ${GRUB_CFG}
# CIS 1.5.2 Check permissions on /boot/grub2/grub.cfg
func_wrapper check_grub_perms
# CIS 1.5.3 Check permissions on /boot/grub2/grub.cfg
func_wrapper check_boot_pass
# TO DO CIS 1.6.1
func_wrapper restrict_core_dumps
# CIS 1.6.2 Verify that the flag to force randomized virtual memory region placement is set
func_wrapper chk_sysctl kernel.randomize_va_space 2
# TO DO CIS 1.7
func_wrapper chk_latest_rel
# CIS 2.1.1 Check telnet-server RPM is not installed
func_wrapper rpm_not_installed telnet-server
# CIS 2.1.2 Check telnet RPM is not installed
func_wrapper rpm_not_installed telnet
# CIS 2.1.3 Check rsh-server RPM is not installed
func_wrapper rpm_not_installed rsh-server
# CIS 2.1.4 Check rsh RPM is not installed
func_wrapper rpm_not_installed rsh
# CIS 2.1.5 Check ypbind RPM is not installed
func_wrapper rpm_not_installed ypbind
# CIS 2.1.6 Check ypserv RPM is not installed
func_wrapper rpm_not_installed ypserv
# CIS 2.1.7 Check tftp RPM is not installed
func_wrapper rpm_not_installed tftp
# CIS 2.1.8 Check tftp-server RPM is not installed
func_wrapper rpm_not_installed tftp-server
# CIS 2.1.9 Check talk RPM is not installed
func_wrapper rpm_not_installed talk
# CIS 2.1.10 Check talk-server RPM is not installed
func_wrapper rpm_not_installed talk-server
# CIS 2.1.11 Check xinetd RPM is not installed
func_wrapper rpm_not_installed xinetd
# CIS 2.1.12 Check chargen-dgram is not enabled
func_wrapper check_svc_not_enabled chargen-dgram
# CIS 2.1.13 Check chargen-stream is not enabled
func_wrapper check_svc_not_enabled chargen-stream
# CIS 2.1.14 Check daytime-dgram is not enabled
func_wrapper check_svc_not_enabled daytime-dgram
# CIS 2.1.15 Check daytime-stream is not enabled
func_wrapper check_svc_not_enabled daytime-stream
# CIS 2.1.16 Check echo-dgram is not enabled
func_wrapper check_svc_not_enabled echo-dgram
# CIS 2.1.17 Check echo-dgram is not enabled
func_wrapper check_svc_not_enabled echo-stream
# CIS 2.1.18 Check tcpmux-server is not enabled
func_wrapper check_svc_not_enabled tcpmux-server
# CIS 3.1 Check Daemon umask
func_wrapper check_umask
# TODO CIS 3.2 X Window System
func_wrapper check_def_tgt
# CIS 3.2 Check telnet-server RPM is not installed
func_wrapper rpm_not_installed xorg-x11-server-common
# CIS 3.3 Check avahi-daemon is not enabled
func_wrapper check_svc_not_enabled avahi-daemon
# CIS 3.4 Check cups is not enabled
func_wrapper check_svc_not_enabled cups
# CIS 3.5 Check dhcp RPM is not installed
func_wrapper rpm_not_installed dhcp
# CIS 3.6 NTP config
func_wrapper ntp_cfg
# CIS 3.7.1 Check LDAP RPMs are not installed
func_wrapper rpm_not_installed openldap-servers
func_wrapper rpm_not_installed openldap-clients
# CIS 3.8 Check NFS and RPC are not enabled
func_wrapper check_svc_not_enabled nfslock
func_wrapper check_svc_not_enabled rpcgssd
func_wrapper check_svc_not_enabled rpcbind
func_wrapper check_svc_not_enabled rpcidmapd
func_wrapper check_svc_not_enabled rpcsvcgssd
# CIS 3.9 Check bind RPM is not installed
func_wrapper rpm_not_installed bind
# CIS 3.10 Check vsftpd RPM is not installed
func_wrapper rpm_not_installed vsftpd
# CIS 3.11 Check httpd RPM is not installed
func_wrapper rpm_not_installed httpd
# CIS 3.12 Check dovecot RPM is not installed
func_wrapper rpm_not_installed dovecot
# CIS 3.13 Check samba RPM is not installed
func_wrapper rpm_not_installed samba
# CIS 3.14 Check squid RPM is not installed
func_wrapper rpm_not_installed squid
# CIS 3.15 Check net-snmp RPM is not installed
func_wrapper rpm_not_installed net-snmp
# CIS 3.16 MTA in local-only mode
# Disable for now as function requires netstat and netstat is not part of the build
#func_wrapper mta_local_only
# CIS 4.1.1 IP Forwarding should be disabled
func_wrapper chk_sysctl net.ipv4.ip_forward 0
# CIS 4.1.2 Send Packet Redirects should be disabled
func_wrapper chk_sysctl net.ipv4.conf.all.send_redirects 0
func_wrapper chk_sysctl net.ipv4.conf.default.send_redirects 0
# CIS 4.2.1 Source Routed Packet Acceptance should be disabled
func_wrapper chk_sysctl net.ipv4.conf.all.accept_source_route 0
func_wrapper chk_sysctl net.ipv4.conf.default.accept_source_route 0
# CIS 4.2.2 ICMP Redirect Acceptance should be disabled
func_wrapper chk_sysctl net.ipv4.conf.all.accept_redirects 0
func_wrapper chk_sysctl net.ipv4.conf.default.accept_redirects 0
# CIS 4.2.3 ICMP Redirect Acceptance should be disabled
func_wrapper chk_sysctl net.ipv4.conf.all.secure_redirects 0
func_wrapper chk_sysctl net.ipv4.conf.all.secure_redirects 0
func_wrapper chk_sysctl net.ipv4.conf.default.secure_redirects 0
# CIS 4.2.4 Log Suspicious Packets
func_wrapper chk_sysctl net.ipv4.conf.all.log_martians 1
func_wrapper chk_sysctl net.ipv4.conf.default.log_martians 1
# CIS 4.2.5 Ignore Broadcast Requests should be enabled
func_wrapper chk_sysctl net.ipv4.icmp_echo_ignore_broadcasts 1
# CIS 4.2.6 Bad Error Message Protection should be enabled
func_wrapper chk_sysctl net.ipv4.icmp_ignore_bogus_error_responses 1
# CIS 4.2.7 RFC-recommended Source Route Validation should be enabled
func_wrapper chk_sysctl net.ipv4.conf.all.rp_filter 1
func_wrapper chk_sysctl net.ipv4.conf.default.rp_filter 1
# CIS 4.2.8 TCP SYN Cookies should be enabled
func_wrapper chk_sysctl net.ipv4.tcp_syncookies 1
# TODO CIS 4.3.1 Check Wireless Interfaces are deactivated
# CIS 4.4.1.1 If IPv6 enabled IPv6 Router Advertisements should be disabled
func_wrapper ip6_router_advertisements_dis
# CIS 4.4.1.2 If IPv6 enabled IPv6 Redirect Acceptance should be disabled
func_wrapper ip6_redirect_accept_dis
# CIS 4.4.2 IPv6 disabled
func_wrapper chk_sysctl net.ipv6.conf.all.disable_ipv6 1
# CIS 4.5.1 Check that TCP Wrappers are installed
func_wrapper rpm_installed tcp_wrappers
# CIS 4.5.2 Check that /etc/hosts.allow exists
func_wrapper chk_file_exists ${HOSTS_ALLOW}
# CIS 4.5.3 Verify permissions on /etc/hosts.allow
func_wrapper check_root_owns ${HOSTS_ALLOW}
func_wrapper check_file_perms ${HOSTS_ALLOW} 644
# CIS 4.5.4 Check that /etc/hosts.deny exists
func_wrapper chk_file_exists ${HOSTS_DENY}
func_wrapper chk_hosts_deny_content
func_wrapper check_root_owns ${HOSTS_DENY}
func_wrapper check_file_perms ${HOSTS_DENY} 644
# CIS 4.6.1 Check that CIS.conf file should disable uncommon network protocol dccp
func_wrapper chk_cis_cnf dccp ${CIS_CNF}
# CIS 4.6.2 Check that CIS.conf file should disable uncommon network protocol sctp
func_wrapper chk_cis_cnf sctp ${CIS_CNF}
# CIS 4.6.3 Check that CIS.conf file should disable uncommon network protocol rds
func_wrapper chk_cis_cnf rds ${CIS_CNF}
# CIS 4.6.4 Check that CIS.conf file should disable uncommon network protocol tipc
func_wrapper chk_cis_cnf tipc ${CIS_CNF}
# CIS 4.7 Firewalld should be enabled
func_wrapper check_svc_enabled firewalld
# CIS 5.1.1 Check that rsyslog is installed
func_wrapper rpm_installed rsyslog
# CIS 5.1.2 rsyslog should be enabled
func_wrapper check_svc_enabled rsyslog
# CIS 5.1.3 Configure rsyslog.conf
# This is too environment specific to audit for here
# CIS 5.1.4 Check perms on rsyslog.conf
func_wrapper chk_file_exists ${RSYSLOG_CNF}
func_wrapper check_root_owns ${RSYSLOG_CNF}
func_wrapper check_file_perms ${RSYSLOG_CNF} 600
# CIS 5.1.5 rsyslog.conf sending logs to a remote host
func_wrapper chk_rsyslog_content
# CIS 5.1.6 This benchmark is only applicable to rsyslog loghosts
# CIS 5.2.1.1 Audit Log Storage Size should be configured
func_wrapper audit_log_storage_size
# CIS 5.2.1.2 Disable System on Audit Log Full
func_wrapper dis_on_audit_log_full
# CIS 5.2.1.3 Disable System on Audit Log Full
func_wrapper keep_all_audit_info
# CIS 5.2.2 auditd should be enabled
func_wrapper check_svc_enabled auditd
# CIS 5.2.3
func_wrapper audit_procs_prior_2_auditd
# CIS 5.2.4 Record events that modify date & time info
func_wrapper audit_date_time
# CIS 5.2.5 Record events that modify user & group info
func_wrapper audit_user_group
# CIS 5.2.6 Record events that modify the system's network env
func_wrapper audit_network_env
# CIS 5.2.7 Record events that modify the system's Mandatory Access Controls
func_wrapper audit_sys_mac
# CIS 5.2.8 Verify Collection Login and Logout events is configured
func_wrapper audit_logins_logouts
# CIS 5.2.9 Verify Collection of Session Initiation info is configured
func_wrapper audit_session_init
# CIS 5.2.10 Verify Collection of Discretionary Access Control permission modification events
func_wrapper audit_dac_perm_mod_events
# CIS 5.2.11 Verify collection of unsuccessful unauthorized access attempts to files
func_wrapper unsuc_unauth_acc_attempts
# CIS 5.2.12 Verify collection of privileged commands
func_wrapper coll_priv_cmds
# CIS 5.2.13 Verify collection of privileged commands
func_wrapper coll_suc_fs_mnts
# CIS 5.2.14 Verify collection of privileged commands
func_wrapper coll_file_del_events
# CIS 5.2.15 Verify collection of privileged commands
func_wrapper coll_chg2_sysadm_scope
# CIS 5.2.16 Verify collection of privileged commands
func_wrapper coll_sysadm_actions
# CIS 5.2.17 Verify collection of Kernel Module Loading and Unloading
func_wrapper kmod_lod_unlod
# CIS 5.2.18 Verify collection of Kernel Module Loading and Unloading
func_wrapper audit_cfg_immut
# CIS 5.3 Log rotate should be configured
func_wrapper logrotate_cfg
# CIS 6.1.1 cron and anacron config
func_wrapper rpm_installed cronie-anacron
# CIS 6.1.2 enable crond daemon
func_wrapper check_svc_enabled crond
# CIS 6.1.3-6.1.8 user/group owner and perms on
# /etc/anacrontab /etc/crontab /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly /etc/cron.d
for file in ${ANACRONTAB} ${CRONTAB} ${CRON_HOURLY} ${CRON_DAILY} ${CRON_WEEKLY} ${CRON_MONTHLY} ${CRON_DIR} ; do
func_wrapper check_root_owns "${file}"
func_wrapper check_file_perms "${file}" 600
done
# CIS 6.1.10 at daemon config
func_wrapper atd_cfg
# CIS 6.1.11 restrict at/cron to authorized users
func_wrapper at_cron_auth_users
# CIS 6.2.1 SSH protocol should be 2
func_wrapper chk_param "${SSHD_CFG}" Protocol 2
# CIS 6.2.2 LogLevel to INFO
func_wrapper chk_param "${SSHD_CFG}" LogLevel INFO
# CIS 6.2.3 Permissions on sshd_config
func_wrapper check_root_owns "${SSHD_CFG}"
func_wrapper check_file_perms "${SSHD_CFG}" 600
# CIS 6.2.4 SSH X11Forwarding
func_wrapper chk_param "${SSHD_CFG}" X11Forwarding no
# CIS 6.2.5 SSH MaxAuthTries
func_wrapper ssh_maxauthtries 4
# CIS 6.2.6 SSH X11Forwarding
func_wrapper chk_param "${SSHD_CFG}" IgnoreRhosts yes
# CIS 6.2.7 SSH HostbasedAuthentication
func_wrapper chk_param "${SSHD_CFG}" HostbasedAuthentication no
# CIS 6.2.8 SSH PermitRootLogin
func_wrapper chk_param "${SSHD_CFG}" PermitRootLogin no
# CIS 6.2.9 SSH PermitEmptyPasswords
func_wrapper chk_param "${SSHD_CFG}" PermitEmptyPasswords no
# CIS 6.2.10 SSH PermitUserEnvironment
func_wrapper chk_param "${SSHD_CFG}" PermitUserEnvironment no
# CIS 6.2.11 SSH Ciphers
func_wrapper chk_param "${SSHD_CFG}" Ciphers aes128-ctr,aes192-ctr,aes256-ctr
# CIS 6.2.12 SSH Idle Timeout Interval for User Login
func_wrapper chk_param "${SSHD_CFG}" ClientAliveInterval 300
func_wrapper chk_param "${SSHD_CFG}" ClientAliveCountMax 0
# CIS 6.2.13 Limit access to SSH
func_wrapper ssh_access
# CIS 6.2.14 SSH banner
func_wrapper chk_param "${SSHD_CFG}" Banner /etc/issue.net
# CIS 6.3.1 SHA-512 password hashing algorithm
func_wrapper pass_hash_algo sha512
# CIS 6.3.2 password creation requirement parameters using pam_pwquality
func_wrapper pass_req_params
# CIS 6.3.3 Set lockout for failed password attempts
func_wrapper failed_pass_lock
# CIS 6.3.4 Limit password reuse
func_wrapper lim_passwd_reuse
# CIS 6.4 Restrict root login to system console
# This is too env specific to put a check in for
# CIS 6.5 Restrict access to su
func_wrapper su_access
# CIS 7.1.1 Password expiration days
func_wrapper chk_param "${LOGIN_DEFS}" PASS_MAX_DAYS 90
# CIS 7.1.2 Password change minimum number of days
func_wrapper chk_param "${LOGIN_DEFS}" PASS_MIN_DAYS 7
# CIS 7.1.3 Password expiring warning days
func_wrapper chk_param "${LOGIN_DEFS}" PASS_WARN_AGE 7
# CIS 7.2 Disable System Accounts
func_wrapper dis_sys_accs
# CIS 7.3 Default group for root account
func_wrapper root_def_grp
# CIS 7.4 Default group for root account
func_wrapper def_umask_for_users
# CIS 7.5 Inactive User accounts should be locked
func_wrapper inactive_usr_acs_locked
# CIS 8.1 user/group owner and perms on
# /etc/motd /etc/issue /etc/issue.net
for file in ${MOTD} ${ISSUE} ${ISSUE_NET} ; do
func_wrapper check_root_owns "${file}"
func_wrapper check_file_perms "${file}" 644
done
# CIS 8.2 OS Information should not be in Login Warning Banners
func_wrapper warning_banners
# CIS 8.3 Set GNOME Warning Banner
# On a host aiming to meet CIS requirements GNOME is unlikely to be installed
# Thus the function says if the file exists then it should have these lines in it
func_wrapper gnome_banner
# CIS 9.1.2 Verify perms on /etc/passwd
func_wrapper check_file_perms "${PASSWD}" 644
# CIS 9.1.3 Verify perms on /etc/shadow
func_wrapper check_file_perms "${SHADOW}" 0
# CIS 9.1.4 Verify perms on /etc/gshadow
func_wrapper check_file_perms "${GSHADOW}" 0
# CIS 9.1.5 Verify perms on /etc/group
func_wrapper check_file_perms "${GROUP}" 644
# CIS 9.1.6-9 user/group owner and perms on /etc/passwd /etc/shadow /etc/gshadow /etc/group
for file in ${PASSWD} ${SHADOW} ${GSHADOW} ${GROUP} ; do
func_wrapper check_root_owns "${file}"
done
# CIS 9.1.10 Shouldn't have any world writable files
func_wrapper world_w_dirs
# CIS 9.1.11 Shouldn't have any unowned files & dirs
func_wrapper unowned_files
# CIS 9.1.12 Shouldn't have any ungrouped files & dirs
func_wrapper ungrouped_files
# CIS 9.1.13 Check for suid executables
func_wrapper suid_exes
# CIS 9.1.14 Check for sgid executables
func_wrapper sgid_exes
# CIS 9.2.1 Ensure password fields are not empty
func_wrapper passwd_field_chk
# CIS 9.2.2 Verify no legacy "+" entries exist in /etc/passwd
func_wrapper nis_in_file ${PASSWD}
# CIS 9.2.3 Verify no legacy "+" entries exist in /etc/shadow
func_wrapper nis_in_file ${SHADOW}
# CIS 9.2.4 Verify no legacy "+" entries exist in /etc/group
func_wrapper nis_in_file ${GROUP}
# CIS 9.2.5 Verify no UID 0 accounts exist other than root
func_wrapper no_uid0_other_root
# CIS 9.2.6 Ensure root PATH integrity
func_wrapper root_path
# CIS 9.2.7 Home dir perms
func_wrapper home_dir_perms
# CIS 9.2.8 User dot file permissions
func_wrapper dot_file_perms
# CIS 9.2.9 User .netrc permissions
func_wrapper dot_netrc_perms
# CIS 9.2.10 User .rhosts files
func_wrapper dot_rhosts_files
# CIS 9.2.11 User .rhosts files
func_wrapper chk_groups_passwd
# CIS 9.2.12 User .rhosts files
func_wrapper chk_home_dirs_exist
# CIS 9.2.13 Check home directory ownership
func_wrapper chk_home_dirs_owns
# CIS 9.2.14 Check for Duplicate UIDs in /etc/passwd
func_wrapper duplicate_uids
# CIS 9.2.15 Check for Duplicate GIDs in /etc/group
func_wrapper duplicate_gids
# CIS 9.2.16 Check that reserved uids are assigned
func_wrapper chk_uids_4_res
# CIS 9.2.17 Check for Duplicate usernames in /etc/passwd
func_wrapper duplicate_usernames
# CIS 9.2.18 Check for Duplicate groupnames in /etc/group
func_wrapper duplicate_groupnames
# CIS 9.2.19 Check for presence of user .netrc files
func_wrapper user_dot_netrc
# CIS 9.2.20 Check for presence of user .forward files
func_wrapper user_dot_forward
}
main