Remove suid / gid and execute permission for 'group' and 'others'.

Similar to: chmod og-ugx /path/to/filename

Removing execution permission is useful to make binaries such as 'su' fail closed rather
than fail open if suid was removed from these.

Do not remove read access since no security benefit and easier to manually undo for users.

chmod 744
This commit is contained in:
Patrick Schleizer 2019-12-22 19:42:40 -05:00
parent 58a4e0bc7d
commit f4b1df02ee
No known key found for this signature in database
GPG key ID: CB8D50BB77BB3C48

View file

@ -52,125 +52,120 @@ echo_wrapper_silent_audit() {
} }
add_nosuid_statoverride_entry() { add_nosuid_statoverride_entry() {
local fso_to_process local fso_to_process
fso_to_process="$fso" fso_to_process="$fso"
local should_be_counter local should_be_counter
should_be_counter="$(find "$fso_to_process" -perm /u=s,g=s | wc -l)" || true should_be_counter="$(find "$fso_to_process" -perm /u=s,g=s | wc -l)" || true
local counter_actual local counter_actual
counter_actual=0 counter_actual=0
local line local line
while read -r line; do while read -r line; do
true "line: $line" true "line: $line"
counter_actual="$(( counter_actual + 1 ))" counter_actual="$(( counter_actual + 1 ))"
local arr file_name existing_mode existing_owner existing_group local arr file_name existing_mode existing_owner existing_group
arr=($line) arr=($line)
file_name="${arr[0]}" file_name="${arr[0]}"
existing_mode="${arr[1]}" existing_mode="${arr[1]}"
existing_owner="${arr[2]}" existing_owner="${arr[2]}"
existing_group="${arr[3]}" existing_group="${arr[3]}"
if [ "$arr" = "" ]; then if [ "$arr" = "" ]; then
echo "ERROR: arr is empty. line: '$line'" >&2 echo "ERROR: arr is empty. line: '$line'" >&2
continue continue
fi fi
if [ "$file_name" = "" ]; then if [ "$file_name" = "" ]; then
echo "ERROR: file_name is empty. line: '$line'" >&2 echo "ERROR: file_name is empty. line: '$line'" >&2
continue continue
fi fi
if [ "$existing_mode" = "" ]; then if [ "$existing_mode" = "" ]; then
echo "ERROR: existing_mode is empty. line: '$line'" >&2 echo "ERROR: existing_mode is empty. line: '$line'" >&2
continue continue
fi fi
if [ "$existing_owner" = "" ]; then if [ "$existing_owner" = "" ]; then
echo "ERROR: existing_owner is empty. line: '$line'" >&2 echo "ERROR: existing_owner is empty. line: '$line'" >&2
continue continue
fi fi
if [ "$existing_group" = "" ]; then if [ "$existing_group" = "" ]; then
echo "ERROR: existing_group is empty. line: '$line'" >&2 echo "ERROR: existing_group is empty. line: '$line'" >&2
continue continue
fi
## -h file True if file is a symbolic Link.
## -u file True if file has its set-user-id bit set.
## -g file True if file has its set-group-id bit set.
if test -h "$file_name" ; then
## https://forums.whonix.org/t/kernel-hardening/7296/323
true "skip symlink: $file_name"
continue
fi
if test -d "$file_name" ; then
true "skip directory: $file_name"
continue
fi
local setuid setuid_output setsgid setsgid_output
setuid=""
setuid_output=""
if test -u "$file_name" ; then
setuid=true
setuid_output="set-user-id"
fi
setsgid=""
setsgid_output=""
if test -g "$file_name" ; then
setsgid=true
setsgid_output="set-group-id"
fi
if [ "$setuid" = "true" ] || [ "$setsgid" = "true" ]; then
string_length_of_existing_mode="${#existing_mode}"
if [ "$string_length_of_existing_mode" = "4" ]; then
new_mode="${existing_mode:1}"
else
new_mode="$existing_mode"
fi fi
## Remove 'others' / 'group' execution ('chmod og-x /path/to/binary') rights for better usability? ## -h file True if file is a symbolic Link.
## Make binaries such as 'su' fail closed rather than fail open if suid was removed from these? ## -u file True if file has its set-user-id bit set.
## Are there suid or sgid binaries which are still useful if suid / sgid has been removed from these? ## -g file True if file has its set-group-id bit set.
## https://forums.whonix.org/t/permission-hardening/8655/10
# if [ "$new_mode" = "755" ]; then if test -h "$file_name" ; then
# new_mode=744 ## https://forums.whonix.org/t/kernel-hardening/7296/323
# fi true "skip symlink: $file_name"
# if [ "$new_mode" = "754" ]; then continue
# new_mode=744 fi
# fi
# if [ "$new_mode" = "745" ]; then if test -d "$file_name" ; then
# new_mode=744 true "skip directory: $file_name"
# fi continue
fi
local setuid setuid_output setsgid setsgid_output
setuid=""
setuid_output=""
if test -u "$file_name" ; then
setuid=true
setuid_output="set-user-id"
fi
setsgid=""
setsgid_output=""
if test -g "$file_name" ; then
setsgid=true
setsgid_output="set-group-id"
fi
local setuid_or_setsgid
setuid_or_setsgid=""
if [ "$setuid" = "true" ] || [ "$setsgid" = "true" ]; then
setuid_or_setsgid=true
fi
if [ "$setuid_or_setsgid" = "" ]; then
continue
fi
## Remove suid / gid and execute permission for 'group' and 'others'.
## Similar to: chmod og-ugx /path/to/filename
## Removing execution permission is useful to make binaries such as 'su' fail closed rather
## than fail open if suid was removed from these.
## Do not remove read access since no security benefit and easier to manually undo for users.
## Are there suid or sgid binaries which are still useful if suid / sgid has been removed from these?
new_mode="744"
local is_whitelisted local is_whitelisted
is_whitelisted="" is_whitelisted=""
for white_list_entry in $whitelist ; do for white_list_entry in $whitelist ; do
if [ "$file_name" = "$white_list_entry" ]; then if [ "$file_name" = "$white_list_entry" ]; then
is_whitelisted="true" is_whitelisted="true"
## Stop looping through the whitelist. ## Stop looping through the whitelist.
break break
fi fi
done done
local is_match_whitelisted local is_match_whitelisted
is_match_whitelisted="" is_match_whitelisted=""
for matchwhite_list_entry in $matchwhitelist ; do for matchwhite_list_entry in $matchwhitelist ; do
if echo "$file_name" | grep -q "$matchwhite_list_entry" ; then if echo "$file_name" | grep -q "$matchwhite_list_entry" ; then
is_match_whitelisted="true" is_match_whitelisted="true"
## Stop looping through the matchwhitelist. ## Stop looping through the matchwhitelist.
break break
fi fi
done done
if [ "$is_whitelisted" = "true" ]; then if [ "$is_whitelisted" = "true" ]; then
echo "INFO: SKIP whitelisted - $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode'" echo "INFO: SKIP whitelisted - $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode'"
continue continue
fi fi
if [ "$is_match_whitelisted" = "true" ]; then if [ "$is_match_whitelisted" = "true" ]; then
echo "INFO: SKIP matchwhitelisted - $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode' | matchwhite_list_entry: '$matchwhite_list_entry'" echo "INFO: SKIP matchwhitelisted - $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode' | matchwhite_list_entry: '$matchwhite_list_entry'"
continue continue
fi fi
echo "INFO: $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode' | new_mode: '$new_mode'" echo "INFO: $setuid_output $setsgid_output found - file_name: '$file_name' | existing_mode: '$existing_mode' | new_mode: '$new_mode'"
@ -198,20 +193,19 @@ add_nosuid_statoverride_entry() {
## Not using --update as this is only for recording. ## Not using --update as this is only for recording.
echo_wrapper_silent_audit dpkg-statoverride $dpkg_admindir_parameter_new_mode --add "$existing_owner" "$existing_group" "$new_mode" "$file_name" echo_wrapper_silent_audit dpkg-statoverride $dpkg_admindir_parameter_new_mode --add "$existing_owner" "$existing_group" "$new_mode" "$file_name"
fi
## /lib will hit ARG_MAX if using bash 'shopt -s globstar' and '/lib/**'. ## /lib will hit ARG_MAX if using bash 'shopt -s globstar' and '/lib/**'.
## Using 'find' with '-perm /u=s,g=s' is faster and avoids ARG_MAX. ## Using 'find' with '-perm /u=s,g=s' is faster and avoids ARG_MAX.
## https://forums.whonix.org/t/disable-suid-binaries/7706/17 ## https://forums.whonix.org/t/disable-suid-binaries/7706/17
done < <( find "$fso_to_process" -perm /u=s,g=s -print0 | xargs -I{} -0 stat -c "%n %a %U %G" {} ) done < <( find "$fso_to_process" -perm /u=s,g=s -print0 | xargs -I{} -0 stat -c "%n %a %U %G" {} )
## Sanity test. ## Sanity test.
if [ ! "$should_be_counter" = "$counter_actual" ]; then if [ ! "$should_be_counter" = "$counter_actual" ]; then
echo "INFO: fso_to_process: '$fso_to_process' | counter_actual : '$counter_actual'" echo "INFO: fso_to_process: '$fso_to_process' | counter_actual : '$counter_actual'"
echo "INFO: fso_to_process: '$fso_to_process' | should_be_counter: '$should_be_counter'" echo "INFO: fso_to_process: '$fso_to_process' | should_be_counter: '$should_be_counter'"
exit_code=202 exit_code=202
echo "ERROR: counter does not check out." >&2 echo "ERROR: counter does not check out." >&2
fi fi
} }
set_file_perms() { set_file_perms() {