From b41df080cf8313dde1134701c4ea28fc25f38e69 Mon Sep 17 00:00:00 2001
From: Charles Herlin <charles.herlin@corp.ovh.com>
Date: Thu, 9 Nov 2017 15:45:42 +0100
Subject: [PATCH] Add sudo management in main and utils

    * perform readonly checks as a regular user
    * sudo -n is used for checks requiring root privileges
    * increase accountability by providing log of individual access to sensitive files
---
 README.md                                     |  5 +++
 bin/hardening.sh                              | 23 +++++++---
 bin/hardening/12.10_find_suid_files.sh        |  2 +-
 bin/hardening/12.11_find_sgid_files.sh        |  2 +-
 .../12.7_find_world_writable_file.sh          |  2 +-
 bin/hardening/12.8_find_unowned_files.sh      |  2 +-
 bin/hardening/12.9_find_ungrouped_files.sh    |  2 +-
 .../13.1_remove_empty_password_field.sh       |  2 +-
 .../13.3_remove_legacy_shadow_entries.sh      |  2 +-
 .../2.17_sticky_bit_world_writable_folder.sh  |  2 +-
 bin/hardening/4.2_enable_nx_support.sh        |  2 +-
 bin/hardening/6.15_mta_localhost.sh           |  2 +-
 bin/hardening/8.1.10_record_dac_edit.sh       |  8 +++-
 .../8.1.11_record_failed_access_file.sh       |  8 +++-
 .../8.1.12_record_privileged_commands.sh      | 11 ++++-
 .../8.1.13_record_successful_mount.sh         |  8 +++-
 bin/hardening/8.1.14_record_file_deletions.sh |  8 +++-
 bin/hardening/8.1.15_record_sudoers_edit.sh   |  8 +++-
 bin/hardening/8.1.16_record_sudo_usage.sh     |  8 +++-
 bin/hardening/8.1.17_record_kernel_modules.sh |  8 +++-
 bin/hardening/8.1.18_freeze_auditd_conf.sh    |  8 +++-
 bin/hardening/8.1.4_record_date_time_edit.sh  |  8 +++-
 bin/hardening/8.1.5_record_user_group_edit.sh |  8 +++-
 bin/hardening/8.1.6_record_network_edit.sh    |  8 +++-
 bin/hardening/8.1.7_record_mac_edit.sh        |  8 +++-
 bin/hardening/8.1.8_record_login_logout.sh    |  8 +++-
 bin/hardening/8.1.9_record_session_init.sh    |  8 +++-
 cisharden.sudoers                             | 23 ++++++++++
 lib/main.sh                                   |  4 ++
 lib/utils.sh                                  | 42 +++++++++----------
 30 files changed, 187 insertions(+), 53 deletions(-)
 create mode 100644 cisharden.sudoers

diff --git a/README.md b/README.md
index 01ba138..a0b7731 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,11 @@ configuration. It will run all scripts in audit mode. If a script passes,
 it will automatically be enabled for future runs. Do NOT use this option
 if you have already started to customize your configuration.
 
+``--sudo``: Audit your system as a normal user, but allow sudo escalation to read
+specific root read-only files. You need to provide a sudoers file in /etc/sudoers.d/
+with NOPASWD option, since checks are executed with ``sudo -n`` option, that will
+not prompt for a password.
+
 ## Hacking
 
 **Getting the source**
diff --git a/bin/hardening.sh b/bin/hardening.sh
index eb0bd2f..914d964 100755
--- a/bin/hardening.sh
+++ b/bin/hardening.sh
@@ -22,6 +22,7 @@ AUDIT_ALL=0
 AUDIT_ALL_ENABLE_PASSED=0
 ALLOW_SERVICE_LIST=0
 SET_HARDENING_LEVEL=0
+SUDO_MODE=''
 
 usage() {
     cat << EOF
@@ -83,6 +84,13 @@ OPTIONS:
         The test number is the numbered prefix of the script,
         i.e. the test number of 1.2_script_name.sh is 1.2.
 
+    --sudo
+        This option lets you audit your system as a normal user, but allows sudo
+        escalation to gain read-only access to root files. Note that you need to
+        provide a sudoers file with NOPASSWD option in /etc/sudoers.d/ because
+        the '-n' option instructs sudo not to prompt for a password.
+        Finally note that '--sudo' mode only works for audit mode.
+
 EOF
     exit 0
 }
@@ -124,6 +132,9 @@ while [[ $# > 0 ]]; do
             TEST_LIST[${#TEST_LIST[@]}]="$2"
             shift
         ;;
+        --sudo)
+            SUDO_MODE='--sudo'
+        ;;
         -h|--help)
             usage
         ;;
@@ -197,14 +208,14 @@ for SCRIPT in $(ls $CIS_ROOT_DIR/bin/hardening/*.sh -v); do
     info "Treating $SCRIPT"
     
     if [ $AUDIT = 1 ]; then
-        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit"
-        $SCRIPT --audit
+        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit $SUDO_MODE"
+        $SCRIPT --audit $SUDO_MODE
     elif [ $AUDIT_ALL = 1 ]; then
-        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all"
-        $SCRIPT --audit-all
+        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE"
+        $SCRIPT --audit-all $SUDO_MODE
     elif [ $AUDIT_ALL_ENABLE_PASSED = 1 ]; then
-        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all"
-        $SCRIPT --audit-all
+        debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE"
+        $SCRIPT --audit-all $SUDO_MODE
     elif [ $APPLY = 1 ]; then
         debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT"
         $SCRIPT
diff --git a/bin/hardening/12.10_find_suid_files.sh b/bin/hardening/12.10_find_suid_files.sh
index b9b4168..a361ca8 100755
--- a/bin/hardening/12.10_find_suid_files.sh
+++ b/bin/hardening/12.10_find_suid_files.sh
@@ -16,7 +16,7 @@ HARDENING_LEVEL=2
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if there are suid files"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 -print)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -4000 -print)
     for BINARY in $RESULT; do
         if grep -q $BINARY <<< "$EXCEPTIONS"; then
             debug "$BINARY is confirmed as an exception"
diff --git a/bin/hardening/12.11_find_sgid_files.sh b/bin/hardening/12.11_find_sgid_files.sh
index 59b61c1..9cc815c 100755
--- a/bin/hardening/12.11_find_sgid_files.sh
+++ b/bin/hardening/12.11_find_sgid_files.sh
@@ -16,7 +16,7 @@ HARDENING_LEVEL=2
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if there are sgid files"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -2000 -print)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -2000 -print)
     for BINARY in $RESULT; do
         if grep -q $BINARY <<< "$EXCEPTIONS"; then
             debug "$BINARY is confirmed as an exception"
diff --git a/bin/hardening/12.7_find_world_writable_file.sh b/bin/hardening/12.7_find_world_writable_file.sh
index 5790714..6e24538 100755
--- a/bin/hardening/12.7_find_world_writable_file.sh
+++ b/bin/hardening/12.7_find_world_writable_file.sh
@@ -16,7 +16,7 @@ HARDENING_LEVEL=3
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if there are world writable files"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002 -print 2>/dev/null)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -0002 -print 2>/dev/null)
     if [ ! -z "$RESULT" ]; then
         crit "Some world writable files are present"
         FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ')
diff --git a/bin/hardening/12.8_find_unowned_files.sh b/bin/hardening/12.8_find_unowned_files.sh
index 02e838f..8079917 100755
--- a/bin/hardening/12.8_find_unowned_files.sh
+++ b/bin/hardening/12.8_find_unowned_files.sh
@@ -18,7 +18,7 @@ USER='root'
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if there are unowned files"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nouser -print 2>/dev/null)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -nouser -print 2>/dev/null)
     if [ ! -z "$RESULT" ]; then
         crit "Some unowned files are present"
         FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ')
diff --git a/bin/hardening/12.9_find_ungrouped_files.sh b/bin/hardening/12.9_find_ungrouped_files.sh
index fbf65ee..57175e7 100755
--- a/bin/hardening/12.9_find_ungrouped_files.sh
+++ b/bin/hardening/12.9_find_ungrouped_files.sh
@@ -18,7 +18,7 @@ GROUP='root'
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if there are ungrouped files"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nogroup -print 2>/dev/null)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -nogroup -print 2>/dev/null)
     if [ ! -z "$RESULT" ]; then
         crit "Some ungrouped files are present"
         FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ')
diff --git a/bin/hardening/13.1_remove_empty_password_field.sh b/bin/hardening/13.1_remove_empty_password_field.sh
index 2c33a0e..f86c395 100755
--- a/bin/hardening/13.1_remove_empty_password_field.sh
+++ b/bin/hardening/13.1_remove_empty_password_field.sh
@@ -18,7 +18,7 @@ FILE='/etc/shadow'
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if accounts have an empty password"
-    RESULT=$(cat $FILE | awk -F: '($2 == "" ) { print $1 }')
+    RESULT=$($SUDO_CMD cat $FILE | awk -F: '($2 == "" ) { print $1 }')
     if [ ! -z "$RESULT" ]; then
         crit "Some accounts have an empty password"
         crit $RESULT
diff --git a/bin/hardening/13.3_remove_legacy_shadow_entries.sh b/bin/hardening/13.3_remove_legacy_shadow_entries.sh
index 1ef2b44..f4d4b6f 100755
--- a/bin/hardening/13.3_remove_legacy_shadow_entries.sh
+++ b/bin/hardening/13.3_remove_legacy_shadow_entries.sh
@@ -19,7 +19,7 @@ RESULT=''
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if accounts have a legacy password entry"
-    if grep '^+:' $FILE -q; then
+    if $SUDO_CMD grep '^+:' $FILE -q; then
         RESULT=$(grep '^+:' $FILE)
         crit "Some accounts have a legacy password entry"
         crit $RESULT
diff --git a/bin/hardening/2.17_sticky_bit_world_writable_folder.sh b/bin/hardening/2.17_sticky_bit_world_writable_folder.sh
index 9f6b8fb..68ee59d 100755
--- a/bin/hardening/2.17_sticky_bit_world_writable_folder.sh
+++ b/bin/hardening/2.17_sticky_bit_world_writable_folder.sh
@@ -16,7 +16,7 @@ HARDENING_LEVEL=2
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking if setuid is set on world writable Directories"
-    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null)
+    RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null)
     if [ ! -z "$RESULT" ]; then
         crit "Some world writable directories are not on sticky bit mode!"
         FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ')
diff --git a/bin/hardening/4.2_enable_nx_support.sh b/bin/hardening/4.2_enable_nx_support.sh
index 81aeaa1..f41bb4e 100755
--- a/bin/hardening/4.2_enable_nx_support.sh
+++ b/bin/hardening/4.2_enable_nx_support.sh
@@ -19,7 +19,7 @@ PATTERN='NX[[:space:]]\(Execute[[:space:]]Disable\)[[:space:]]protection:[[:spac
 nx_supported_and_enabled() {
     if grep -q ' nx ' /proc/cpuinfo; then
         # NX supported, but if noexec=off specified, it's not enabled
-        if grep -qi 'noexec=off' /proc/cmdline; then
+        if $SUDO_CMD grep -qi 'noexec=off' /proc/cmdline; then
             FNRET=1 # supported but disabled
         else
             FNRET=0 # supported and enabled
diff --git a/bin/hardening/6.15_mta_localhost.sh b/bin/hardening/6.15_mta_localhost.sh
index f21a86c..1ef1d9c 100755
--- a/bin/hardening/6.15_mta_localhost.sh
+++ b/bin/hardening/6.15_mta_localhost.sh
@@ -17,7 +17,7 @@ HARDENING_EXCEPTION=mail
 # This function will be called if the script status is on enabled / audit mode
 audit () {
     info "Checking netport ports opened"
-    RESULT=$(netstat -an | grep LIST | grep ":25[[:space:]]") || :
+    RESULT=$($SUDO_CMD netstat -an | grep LIST | grep ":25[[:space:]]") || :
     RESULT=${RESULT:-}
     debug "Result is $RESULT"
     if [ -z "$RESULT" ]; then
diff --git a/bin/hardening/8.1.10_record_dac_edit.sh b/bin/hardening/8.1.10_record_dac_edit.sh
index 3559eb7..467c766 100755
--- a/bin/hardening/8.1.10_record_dac_edit.sh
+++ b/bin/hardening/8.1.10_record_dac_edit.sh
@@ -23,16 +23,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.11_record_failed_access_file.sh b/bin/hardening/8.1.11_record_failed_access_file.sh
index f411283..aad2351 100755
--- a/bin/hardening/8.1.11_record_failed_access_file.sh
+++ b/bin/hardening/8.1.11_record_failed_access_file.sh
@@ -21,16 +21,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.12_record_privileged_commands.sh b/bin/hardening/8.1.12_record_privileged_commands.sh
index 856e52c..95e03f4 100755
--- a/bin/hardening/8.1.12_record_privileged_commands.sh
+++ b/bin/hardening/8.1.12_record_privileged_commands.sh
@@ -14,23 +14,30 @@ set -u # One variable unset, it's over
 HARDENING_LEVEL=4
 
 # Find all files with setuid or setgid set
-AUDIT_PARAMS=$(find / -xdev \( -perm -4000 -o -perm -2000 \) -type f | awk '{print \
+SUDO_CMD='sudo -n'
+AUDIT_PARAMS=$($SUDO_CMD find / -xdev \( -perm -4000 -o -perm -2000 \) -type f | awk '{print \
 "-a always,exit -F path=" $1 " -F perm=x -F auid>=1000 -F auid!=4294967295 \
 -k privileged" }')
 FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.13_record_successful_mount.sh b/bin/hardening/8.1.13_record_successful_mount.sh
index c3e411b..d66c00d 100755
--- a/bin/hardening/8.1.13_record_successful_mount.sh
+++ b/bin/hardening/8.1.13_record_successful_mount.sh
@@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.14_record_file_deletions.sh b/bin/hardening/8.1.14_record_file_deletions.sh
index 07d6acb..1889103 100755
--- a/bin/hardening/8.1.14_record_file_deletions.sh
+++ b/bin/hardening/8.1.14_record_file_deletions.sh
@@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.15_record_sudoers_edit.sh b/bin/hardening/8.1.15_record_sudoers_edit.sh
index 6d22771..ea3ebd9 100755
--- a/bin/hardening/8.1.15_record_sudoers_edit.sh
+++ b/bin/hardening/8.1.15_record_sudoers_edit.sh
@@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.16_record_sudo_usage.sh b/bin/hardening/8.1.16_record_sudo_usage.sh
index 489602b..ede8754 100755
--- a/bin/hardening/8.1.16_record_sudo_usage.sh
+++ b/bin/hardening/8.1.16_record_sudo_usage.sh
@@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.17_record_kernel_modules.sh b/bin/hardening/8.1.17_record_kernel_modules.sh
index d6f48f2..deb14f4 100755
--- a/bin/hardening/8.1.17_record_kernel_modules.sh
+++ b/bin/hardening/8.1.17_record_kernel_modules.sh
@@ -21,16 +21,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.18_freeze_auditd_conf.sh b/bin/hardening/8.1.18_freeze_auditd_conf.sh
index 2342621..f6ce1ed 100755
--- a/bin/hardening/8.1.18_freeze_auditd_conf.sh
+++ b/bin/hardening/8.1.18_freeze_auditd_conf.sh
@@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.4_record_date_time_edit.sh b/bin/hardening/8.1.4_record_date_time_edit.sh
index 41de3f5..3110963 100755
--- a/bin/hardening/8.1.4_record_date_time_edit.sh
+++ b/bin/hardening/8.1.4_record_date_time_edit.sh
@@ -22,16 +22,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.5_record_user_group_edit.sh b/bin/hardening/8.1.5_record_user_group_edit.sh
index 1fa0777..a1762a7 100755
--- a/bin/hardening/8.1.5_record_user_group_edit.sh
+++ b/bin/hardening/8.1.5_record_user_group_edit.sh
@@ -22,16 +22,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.6_record_network_edit.sh b/bin/hardening/8.1.6_record_network_edit.sh
index e1e2dc9..22e8533 100755
--- a/bin/hardening/8.1.6_record_network_edit.sh
+++ b/bin/hardening/8.1.6_record_network_edit.sh
@@ -23,16 +23,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.7_record_mac_edit.sh b/bin/hardening/8.1.7_record_mac_edit.sh
index 756f45d..9a26de8 100755
--- a/bin/hardening/8.1.7_record_mac_edit.sh
+++ b/bin/hardening/8.1.7_record_mac_edit.sh
@@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.8_record_login_logout.sh b/bin/hardening/8.1.8_record_login_logout.sh
index 7ee224a..7f886d1 100755
--- a/bin/hardening/8.1.8_record_login_logout.sh
+++ b/bin/hardening/8.1.8_record_login_logout.sh
@@ -20,16 +20,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/bin/hardening/8.1.9_record_session_init.sh b/bin/hardening/8.1.9_record_session_init.sh
index 807721d..9616a4b 100755
--- a/bin/hardening/8.1.9_record_session_init.sh
+++ b/bin/hardening/8.1.9_record_session_init.sh
@@ -20,16 +20,22 @@ FILE='/etc/audit/audit.rules'
 
 # This function will be called if the script status is on enabled / audit mode
 audit () {
-    IFS=$'\n'
+    # define custom IFS and save default one
+    d_IFS=$IFS
+    c_IFS=$'\n'
+    IFS=$c_IFS
     for AUDIT_VALUE in $AUDIT_PARAMS; do
         debug "$AUDIT_VALUE should be in file $FILE"
+        IFS=$d_IFS
         does_pattern_exist_in_file $FILE $AUDIT_VALUE
+        IFS=$c_IFS
         if [ $FNRET != 0 ]; then
             crit "$AUDIT_VALUE is not in file $FILE"
         else
             ok "$AUDIT_VALUE is present in $FILE"
         fi
     done
+    IFS=$d_IFS
 }
 
 # This function will be called if the script status is on enabled mode
diff --git a/cisharden.sudoers b/cisharden.sudoers
new file mode 100644
index 0000000..a66469f
--- /dev/null
+++ b/cisharden.sudoers
@@ -0,0 +1,23 @@
+Cmnd_Alias SCL_CMD =    /bin/grep ,\
+                        /bin/zgrep,\
+                        /bin/cat,\
+                        /usr/bin/stat,\
+                        /usr/bin/getent,\
+                        /usr/bin/[,\
+                        /bin/ls,\
+                        /usr/bin/find,\
+                        ! /usr/bin/find *-exec*, \
+                        ! /usr/bin/find *-delete*,\
+                        /usr/bin/apt-get update -y,\
+                        /usr/bin/apt-get upgrade -s,\
+                        /usr/bin/cut,\
+                        /sbin/iptables -nL,\
+                        /sbin/iptables -nL *,\
+                        /sbin/sysctl net.*,\
+                        /sbin/sysctl fs.*,\
+                        /sbin/sysctl kernel.*,\
+                        /sbin/sysctl -a,\
+                        /bin/dmesg "",\
+                        /bin/netstat
+
+cisharden ALL = (root) NOPASSWD: SCL_CMD
diff --git a/lib/main.sh b/lib/main.sh
index 3d21668..38d8e5e 100644
--- a/lib/main.sh
+++ b/lib/main.sh
@@ -4,6 +4,7 @@ SCRIPT_NAME=${LONG_SCRIPT_NAME%.sh}
 CRITICAL_ERRORS_NUMBER=0 # This will be used to see if a script failed, or passed
 status=""
 forcedstatus=""
+SUDO_CMD=""
 
 [ -r $CIS_ROOT_DIR/lib/constants.sh  ] && . $CIS_ROOT_DIR/lib/constants.sh
 [ -r $CIS_ROOT_DIR/etc/hardening.cfg ] && . $CIS_ROOT_DIR/etc/hardening.cfg
@@ -31,6 +32,9 @@ while [[ $# > 0 ]]; do
             info "Audit argument passed but script is disabled"
         fi
         ;;
+        --sudo)
+        SUDO_CMD="sudo -n"
+        ;;
         *)
             debug "Unknown option passed"
         ;;
diff --git a/lib/utils.sh b/lib/utils.sh
index 71278aa..ab843fb 100644
--- a/lib/utils.sh
+++ b/lib/utils.sh
@@ -8,7 +8,7 @@ has_sysctl_param_expected_result() {
     local SYSCTL_PARAM=$1
     local EXP_RESULT=$2
 
-    if [ "$(sysctl $SYSCTL_PARAM 2>/dev/null)" = "$SYSCTL_PARAM = $EXP_RESULT" ]; then
+    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"
@@ -21,7 +21,7 @@ has_sysctl_param_expected_result() {
 
 does_sysctl_param_exists() {
     local SYSCTL_PARAM=$1
-    if [ "$(sysctl -a 2>/dev/null |grep "$SYSCTL_PARAM" -c)" = 0 ]; then
+    if [ "$($SUDO_CMD sysctl -a 2>/dev/null |grep "$SYSCTL_PARAM" -c)" = 0 ]; then
         FNRET=1
     else
         FNRET=0
@@ -50,7 +50,7 @@ set_sysctl_param() {
 
 does_pattern_exist_in_dmesg() {
     local PATTERN=$1
-    if $(dmesg | grep -qE "$PATTERN"); then
+    if $($SUDO_CMD dmesg | grep -qE "$PATTERN"); then
         FNRET=0
     else
         FNRET=1
@@ -63,7 +63,7 @@ does_pattern_exist_in_dmesg() {
 
 does_file_exist() {
     local FILE=$1
-    if [ -e $FILE ]; then
+    if $SUDO_CMD [ -e $FILE ]; then
         FNRET=0
     else
         FNRET=1
@@ -76,8 +76,8 @@ has_file_correct_ownership() {
     local GROUP=$3
     local USERID=$(id -u $USER)
     local GROUPID=$(getent group $GROUP | cut -d: -f3)
-    debug "stat -c '%u %g' $FILE"
-    if [ "$(stat -c "%u %g" $FILE)" = "$USERID $GROUPID" ]; then
+    debug "$SUDO_CMD stat -c '%u %g' $FILE"
+    if [ "$($SUDO_CMD stat -c "%u %g" $FILE)" = "$USERID $GROUPID" ]; then
         FNRET=0
     else
         FNRET=1
@@ -88,7 +88,7 @@ has_file_correct_permissions() {
     local FILE=$1
     local PERMISSIONS=$2
     
-    if [ $(stat -L -c "%a" $1) = "$PERMISSIONS" ]; then
+    if [ $($SUDO_CMD stat -L -c "%a" $1) = "$PERMISSIONS" ]; then
         FNRET=0
     else
         FNRET=1
@@ -100,9 +100,9 @@ does_pattern_exist_in_file() {
     local PATTERN=$2
 
     debug "Checking if $PATTERN is present in $FILE"
-    if [ -r "$FILE" ] ; then
-        debug "grep -qE -- '$PATTERN' $FILE"
-        if $(grep -qE -- "$PATTERN" $FILE); then
+    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
@@ -189,7 +189,7 @@ does_group_exist() {
 
 is_service_enabled() {
     local SERVICE=$1
-    if [ $(find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then
+    if [ $($SUDO_CMD find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then
         debug "Service $SERVICE is enabled"
         FNRET=0
     else
@@ -209,10 +209,10 @@ is_kernel_option_enabled() {
     if [ $# -ge 2 ] ; then
         MODULE_NAME="$2"
     fi
-    if [ -r "/proc/config.gz" ] ; then
-        RESULT=$(zgrep "^$KERNEL_OPTION=" /proc/config.gz) || :
-    elif [ -r "/boot/config-$(uname -r)" ] ; then
-        RESULT=$(grep "^$KERNEL_OPTION=" "/boot/config-$(uname -r)") || :
+    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
@@ -226,13 +226,13 @@ is_kernel_option_enabled() {
         FNRET=2 # Not found
     fi
 
-    if [ "$FNRET" -ne 0 -a -n "$MODULE_NAME" -a -d "/lib/modules/$(uname -r)" ] ; then
+    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=$(find "/lib/modules/$(uname -r)/" -type f -name "$MODULE_NAME.ko")
-        if [ -n "$modulefile" ] ; then
+        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
@@ -334,10 +334,10 @@ apt_update_if_needed()
         if [ $UPDATE_AGE -gt 21600 ]
         then
             # update too old, refresh database
-            apt-get update -y >/dev/null 2>/dev/null
+            $SUDO_CMD apt-get update -y >/dev/null 2>/dev/null
         fi
     else
-        apt-get update -y >/dev/null 2>/dev/null
+        $SUDO_CMD apt-get update -y >/dev/null 2>/dev/null
     fi
 }
 
@@ -345,7 +345,7 @@ apt_check_updates()
 {
     local NAME="$1"
     local DETAILS="/dev/shm/${NAME}"
-    apt-get upgrade -s 2>/dev/null | grep -E "^Inst" > $DETAILS || : 
+    $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