blob: 6c47b7ca3a7c244a4026f61b605a44d0eb455541 [file] [log] [blame] [raw]
#!/bin/sh
# YesLogin
# Copyright 2015-2024 Rivoreo
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
set -e
setup_dir="${0%/*}"
if [ "$setup_dir" != "$0" ] && [ "$setup_dir" != . ] && [ "$setup_dir" != "$PWD" ] && [ ! "$setup_dir" -ef . ]; then
echo "Error: You must run this program under the directory where it resides" 1>&2
exit 1
fi
unset setup_dir
if [ -z "$HOME" ] || [ ! -d "$HOME" ]; then
echo "Error: Home directory didn't exist, please check the HOME environment variable" 1>&2
exit 1
fi
replace_chattr=
replace_reboot=
replace_tar=
replace_sbin_init=
configure_sudo_io_logging=
install_procd=
install_update_blacklist=
install_chflags=
remove_utempter=
remove_cloud_init=
bind_protection=
harden=
reinstall_config_files=
for a in "$@"; do case "$a" in
--replace-chattr)
replace_chattr=1
;;
--replace-chattr-without-backup)
replace_chattr=2
;;
--replace-reboot)
replace_reboot=1
;;
--replace-reboot-with-reminder)
replace_reboot=2
;;
--replace-tar)
replace_tar=1
;;
--replace-sbin-init)
replace_sbin_init=1
;;
--configure-sudo-io-logging)
configure_sudo_io_logging=1
;;
--install-procd)
install_procd=1
;;
--install-update-blacklist)
install_update_blacklist=1
;;
--install-chflags)
install_chflags=1
;;
--remove-utempter)
remove_utempter=1
;;
--remove-cloud-init)
remove_cloud_init=1
;;
--bind-protection)
bind_protection=1
;;
--harden)
harden=1
;;
--reinstall-config-files)
reinstall_config_files=1
;;
-h|--help)
cat << EOT
Usage: $0 [<options>]
Options:
--replace-chattr[-without-backup]
--replace-reboot[-with-reminder]
--replace-tar
--replace-sbin-init
--configure-sudo-io-logging
--install-procd
--install-update-blacklist
--install-chflags
--remove-utempter
--remove-cloud-init
--bind-protection
--harden
--reinstall-config-files
EOT
exit 0
;;
--)
break
;;
-*)
printf "Error: Unknown option '%s'\\n" "$a" 1>&2
exit 255
;;
*)
printf "Warning: Extra operand '%s'\\n" "$a" 1>&2
;;
esac done
if [ -n "$harden" ] && [ "$replace_chattr" != 2 ]; then
echo "Error: Hardening requires '--replace-chattr-without-backup'" 1>&2
exit 255
fi
need_fix_path=
seen_usr_local_bin=
seen_usr_local_sbin=
seen_usr_bin=
seen_usr_sbin=
seen_bin=
seen_sbin=
ORIG_IFS="$IFS"
IFS=:
for d in $PATH; do case "$d" in
/usr/local/bin)
seen_usr_local_bin=1
if [ -n "$seen_usr_bin" ]; then
printf 'Warning: Inappropriate PATH (%s): /usr/local/bin is placed after /usr/bin, fixing\n' "$PATH" 1>&2
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
seen_usr_local_sbin=1
need_fix_path=1
break
fi
;;
/usr/local/sbin)
seen_usr_local_sbin=1
if [ -n "$seen_usr_sbin" ]; then
printf 'Warning: Inappropriate PATH (%s): /usr/local/sbin is placed after /usr/sbin, fixing\n' "$PATH" 1>&2
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
seen_usr_local_bin=1
need_fix_path=1
break
fi
;;
/usr/bin)
seen_usr_bin=1
;;
/usr/sbin)
seen_usr_sbin=1
;;
/bin)
if [ -z "$seen_usr_bin" ]; then
printf 'Warning: Inappropriate PATH (%s): /bin has seen before /usr/bin, fixing\n' "$PATH" 1>&2
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
seen_usr_local_bin=1
seen_usr_local_sbin=1
need_fix_path=1
break
fi
seen_bin=1
;;
/sbin)
if [ -z "$seen_usr_sbin" ]; then
printf 'Warning: Inappropriate PATH (%s): /sbin has seen before /usr/sbin, fixing\n' "$PATH" 1>&2
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
seen_usr_local_bin=1
seen_usr_local_sbin=1
need_fix_path=1
break
fi
seen_sbin=1
;;
esac done
IFS="$ORIG_IFS"
if [ -z "$seen_usr_local_sbin" ]; then
export PATH="/usr/local/sbin:$PATH"
need_fix_path=1
fi
if [ -z "$seen_usr_local_bin" ]; then
export PATH="/usr/local/bin:$PATH"
need_fix_path=1
fi
unset seen_usr_local_bin
unset seen_usr_local_sbin
unset seen_usr_bin
unset seen_usr_sbin
unset seen_bin
unset seen_sbin
KERNEL_NAME="`uname -s`"
is_gnu() {
[ -x /lib/ld-linux.so.2 ] || [ -x /lib/ld-linux.so.3 ] || [ -x /lib/ld-linux-armhf.so.3 ] || [ -f /lib64/ld-linux-x86-64.so.2 ] && return
getconf GNU_LIBC_VERSION 2> /dev/null | grep -q ^glibc
}
. fileflags/which.sh || true
[ -x /usr/bin/cattr ] && chattr() { cattr "$@"; }
if [ -n "$replace_chattr" ]; then
for f in chattr lsattr; do
if [ "`dd bs=1 count=9 if=/usr/bin/$f 2> /dev/null`" = "#!/bin/sh" ]; then
eval "${f}_replaced=1"
else
eval "${f}_replaced="
fi
done
if [ -n "$chattr_replaced" ] && [ -n "$lsattr_replaced" ]; then
echo "chattr(1) and lsattr(1) appear already been replaced" 1>&2
replace_chattr=
fi
fi
[ "$KERNEL_NAME" = Linux ] && ! which chflags > /dev/null && chflags() { sh fileflags/chflags.ext2ioctl.sh "$@"; }
set_immutable() {
set_immutable_ok=
chflags_output="`chflags simmutable \"$@\" 2>&1`" && set_immutable_ok=1
chattr_output="`chattr +i -- \"$@\" 2>&1`" && set_immutable_ok=1
[ -z "$set_immutable_ok" ] && printf %s\\n%s\\n "$chflags_output" "$chattr_output" 1>&2
unset chflags_output chattr_output
[ -n "$set_immutable_ok" ]
}
set_append_only() {
set_append_only_ok=
chflags_output="`chflags sappend \"$@\" 2>&1`" && set_append_only_ok=1
chattr_output="`chattr +a -- \"$@\" 2>&1`" && set_append_only_ok=1
[ -z "$set_append_only_ok" ] && printf %s\\n%s\\n "$chflags_output" "$chattr_output" 1>&2
unset chflags_output chattr_output
[ -n "$set_append_only_ok" ]
}
install_program() {
[ -f "$2" ] && [ -x "$2" ] && cmp -s -- "$2" "$1" && return
rm -rf -- "$2"
cp -- "$1" "$2"
chmod -- 755 "$2"
[ "$KERNEL_NAME" = Linux ] && chcon -- system_u:object_r:shell_exec_t:s0 "$2" > /dev/null 2>&1 || true
}
if perl_program_path="`which perl 2> /dev/null`"; then
if [ "`dd \"if=$perl_program_path\" bs=11 count=1 2> /dev/null`" = "#!/bin/bash" ]; then
eval "`grep -E '^REAL_PERL=["/a-zA-Z0-9._-]+\$' \"$perl_program_path\"`"
if [ -f "$REAL_PERL" ] && [ -x "$REAL_PERL" ]; then
printf "Found real perl at '%s' from existing perl wrapper\\n" "$REAL_PERL" 1>&2
perl_path="${REAL_PERL%/*}"
export REAL_PERL
else
perl_path=
REAL_PERL=
fi
else
[ -f /var/log/perl.log ] || touch /var/log/perl.log
chmod 666 /var/log/perl.log
set_append_only /var/log/perl.log
perl_path=/usr/lib/perl5
for d in /usr/lib/perl5 /usr/lib/perl /usr/lib64/perl5 /usr/lib64/perl /usr/lib32/perl5 /usr/lib32/perl /usr/local/lib/perl5 /usr/local/lib/perl /opt/perl5 /opt/perl; do
[ -d "$d" ] && perl_path="$d" && break
done
[ -d "$perl_path" ] || mkdir "$perl_path"
mv "$perl_program_path" "$perl_path/"
export REAL_PERL="$perl_path/perl"
sed -E "s ^REAL_PERL=.+ REAL_PERL=$REAL_PERL " perl > "$perl_program_path"
chmod 755 "$perl_program_path"
set_immutable "$perl_program_path" || true
fi
[ -n "$harden" ] && set_immutable "$REAL_PERL"
else
perl_path=
REAL_PERL=
fi
[ -d /var/log/noshell ] || mkdir /var/log/noshell
chmod 1733 /var/log/noshell
[ -f /var/log/shd.log ] || touch /var/log/shd.log
chmod 666 /var/log/shd.log || true
set_append_only /var/log/shd.log
for f in "$HOME/.bashrc" "$HOME/.profile"; do
[ -f "$f" ] && continue
if [ -e "$f" ]; then
printf "Warning: %s isn't a regular file, recreating\\n" "$f" 1>&2
rm -f "$f"
fi
true >> "$f"
done
need_source_system_bashrc=
if [ ! -f "$HOME/.bashrc" ]; then
printf 'Warning: %s is missing! Fixing\n' "$HOME/.bashrc" 1>&2
need_source_system_bashrc=1
elif ! grep -Fq -e /etc/bash/bashrc -e /etc/bash.bashrc -e /etc/bashrc "$HOME/.bashrc"; then
printf 'Warning: Missing reference to system bashrc from %s. Fixing\n' "$HOME/.bashrc" 1>&2
need_source_system_bashrc=1
elif ! grep -Eq "^PROMPT_COMMAND='.+ /dev/shm" "$HOME/.bashrc" && grep -Fq PROMPT_COMMAND= "$HOME/.bashrc"; then
printf 'Warning: PROMPT_COMMAND redefined in %s\n' "$HOME/.bashrc" 1>&2
fi
if [ -n "$need_source_system_bashrc" ]; then
for f in /etc/bash/bashrc /etc/bash.bashrc /etc/bashrc ""; do
if [ -n "$f" ]; then
[ -f "$f" ] || continue
printf '\nsource %s\n' "$f" >> "$HOME/.bashrc"
else
echo "Warning: Cannot find system bashrc file in any known location" 1>&2
printf 'Failed to fix %s\n' "$HOME/.bashrc" 1>&2
fi
break
done
fi
append_uniq_line() {
grep --fixed-strings --line-regexp --quiet -- "$1" "$2" && return
printf %s\\n "$1" >> "$2"
}
chmod u+rw,a+x "$HOME"
if [ -n "$need_fix_path" ]; then
append_uniq_line "export PATH=\"$PATH\"" "$HOME/.profile"
[ -f "$HOME/.bash_profile" ] && append_uniq_line "export PATH=\"$PATH\"" "$HOME/.bash_profile"
fi
if [ -n "$reinstall_config_files" ] || [ ! -f "$HOME/.shdrc" ]; then
cp shdrc "$HOME/.shdrc"
chmod 640 "$HOME/.shdrc"
fi
source_shdrc_code='[ -z "$SHELL_DEF" ] && [ -r "$HOME/.shdrc" ] && dd if="$HOME/.shdrc" of=/dev/null bs=1 count=1 > /dev/null 2>&1 && . "$HOME/.shdrc"'
append_uniq_line "$source_shdrc_code" "$HOME/.bashrc"
append_uniq_line "$source_shdrc_code" "$HOME/.profile"
[ -f "$HOME/.bash_profile" ] && append_uniq_line "$source_shdrc_code" "$HOME/.bash_profile"
[ -f /etc/ssh/sshd_config ] && sftp_server_program=`sed -En 's/^[[:space:]]*Subsystem[[:space:]]+sftp[[:space:]]+//p' /etc/ssh/sshd_config` || sftp_server_program=/usr/lib/openssh/sftp-server
for f in "$HOME/.blacklist" "$HOME/.bash_history" "$HOME/.ash_commands" "$HOME/.login.log"; do
if [ ! -h "$f" ]; then
[ -e "$f" ] || continue
[ -f "$f" ] && continue
fi
rm -rf "$f"
done
if [ -n "$harden" ]; then
set_immutable "$HOME/.shdrc" "$HOME/.bashrc"
printf '%s and %s has been made immutable, you will have to undo this before editing these files\n' "$HOME/.shdrc" "$HOME/.bashrc" 1>&2
[ -f "$HOME/.blacklist" ] || true >> "$HOME/.blacklist"
set_append_only "$HOME/.blacklist"
fi
if [ -n "$reinstall_config_files" ] || [ ! -f /etc/rshrc ]; then
rm -rf /etc/rshrc
cp rshrc /etc/rshrc
chmod 644 /etc/rshrc
fi
install_program noshellhere /bin/noshellhere
if rm -f /bin/mbash; then
sed -E "s#^SFTP_SERVER_PROGRAM=.+#SFTP_SERVER_PROGRAM=$sftp_server_program#" mbash > /bin/mbash
chmod 755 /bin/mbash
[ "$KERNEL_NAME" = Linux ] && chcon system_u:object_r:shell_exec_t:s0 /bin/mbash > /dev/null 2>&1 || true
elif [ ! -f /bin/mbash ] || [ ! -x /bin/mbash ]; then
echo "Failed to remove non-regular or non-executable file /bin/mbash" 1>&2
exit 1
fi
[ -n "$harden" ] && set_immutable /etc/rshrc /bin/noshellhere /bin/mbash
for f in /usr/bin/script /usr/bin/telnet /usr/bin/sudo /usr/bin/fakeroot-ng; do
[ -x "$f" ] || printf "Warning: %s not found\\n" "$f"
done 1>&2
tmp_piao=
rm -f /tmp/true
printf '#!/bin/sh\nexit 0\n' > /tmp/true
chmod 755 /tmp/true
if ! /tmp/true; then
echo "Warning: Cannot execute program under '/tmp'!" 1>&2
echo "Please make sure that '/tmp/' isn't mounted with 'noexec'" 1>&2
tmp_piao=1
fi
rm -f /tmp/true
if grep -Fqs /dev/shm /usr/bin/fakeroot-ng; then
echo "Warning: /usr/bin/fakeroot-ng uses /dev/shm" 1>&2
fi
if [ -d /dev/shm ] && ! grep -Eq 'PROMPT_COMMAND=.+ /dev/shm' /etc/bash/bashrc /etc/bash.bashrc /etc/bashrc > /dev/null 2>&1; then
echo "Warning: /dev/shm is not guarded from bash(1)" 1>&2
fi
for u in pischi dudu ad1tz systemd; do
uid_and_shell="`grep ^$u: /etc/passwd | cut -s -d : -f 3,7`" || continue
[ -z "$uid_and_shell" ] && continue
if printf %s\\n "${uid_and_shell%%:*}" | grep -Eq '^0+$'; then
echo "Warning: user account '$u' exists with UID 0" 1>&2
fi
shell="${uid_and_shell#*:}"
[ -z "$shell" ] && shell=/bin/sh
case "$shell" in
*/bin/noshellhere|*/bin/mbash)
;;
*/bin/false)
;;
*/bin/nologin|*/sbin/nologin)
;;
*)
printf "Warning: user account '%s' exists with a likely non-audited shell '%s'\\n" $u "$shell" 1>&2
;;
esac
unset shell
done
unset u uid_and_shell
if is_gnu; then
[ ! -f /var/log/btmp ] && touch /var/log/btmp && chmod 600 /var/log/btmp
[ -f /var/log/wtmp ] || touch /var/log/wtmp
fi
if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then
systemctl disable --now systemd-tmpfiles-clean systemd-tmpfiles-clean.timer || true
systemctl mask systemd-tmpfiles-clean systemd-tmpfiles-clean.timer || true
fi
if [ -n "$replace_chattr" ]; then
for p in ch ls; do
f=${p}attr
eval "[ -n \"\$${f}_replaced\" ]" && continue
[ -f /usr/bin/$f ] && [ -x /usr/bin/$f ] || continue
new_name=${p%?}attr
if [ ! -h /usr/bin/$new_name ] && [ -f /usr/bin/$new_name ] && [ -s /usr/bin/$new_name ]; then
if [ -s /usr/bin/$f ] && ! cmp /usr/bin/$f /usr/bin/$new_name; then
echo "Error: Both /usr/bin/$f and /usr/bin/$new_name exist" 1>&2
exit 1
fi
rm -f /usr/bin/$f
else
rm -rf /usr/bin/$new_name
mv /usr/bin/$f /usr/bin/$new_name
fi
cp $f /usr/bin/$f
chmod 755 /usr/bin/$f
cattr +i /usr/bin/$f || chflags simmutable /usr/bin/$f
done
chattr() { cattr "$@"; }
fi
if [ -n "$replace_reboot" ]; then
if [ "$replace_reboot" = 2 ]; then
replacement_reboot_program=reboot.reminder
else
replacement_reboot_program=reboot
fi
reboot_program=/usr/sbin/reboot
is_already_replaced=
for f in /usr/sbin/reboot /sbin/reboot /usr/sbin/shutdown /sbin/shutdown; do
[ -f $f ] || continue
[ -x $f ] || continue
[ -h $f ] && continue
if cmp -s $f $replacement_reboot_program; then
reboot_program=$f
is_already_replaced=1
break
fi
if rm -f $f. mv $f $f. && chmod 0 $f.; then
[ -n "$harden" ] && set_immutable $f.
reboot_program=$f
break
fi > /dev/null 2>&1
done
if [ -z "$is_already_replaced" ]; then
rm -f $reboot_program
cp $replacement_reboot_program $reboot_program
chmod 755 $reboot_program
fi
[ -n "$harden" ] && set_immutable $reboot_program
dir=${reboot_program%/*}
reboot_program=${reboot_program##*/}
for f in reboot halt poweroff shutdown; do
[ $f != $reboot_program ] && ln -sf $reboot_program $dir/$f
done
fi
if [ -n "$replace_tar" ]; then
standard_tar_path=
for f in /usr/bin/tar /bin/tar; do [ -x $f ] && standard_tar_path=$f && break; done
if [ -n "$standard_tar_path" ]; then
if tar_in_path="`which tar`" && [ "$tar_in_path" != $standard_tar_path ]; then
i=0
while [ -h "$tar_in_path" ] && tar_in_path="`readlink -- \"$tar_in_path\"`" && [ $i -lt 16 ]
do i=$((i+1)); done
[ ! -h "$tar_in_path" ] && [ "$tar_in_path" != $standard_tar_path ] && chmod a-x -- "$tar_in_path"
fi
if [ "`which tar`" = $standard_tar_path ]; then
if [ "`dd if=$standard_tar_path bs=2 count=1 2> /dev/null`" != "#!" ] &&
tar_version="`tar --version 2> /dev/null`" &&
if printf %s "$tar_version" | grep -Fq -e " (GNU tar" -e " Free Software Foundation"; then
new_path=${standard_tar_path%/tar}/gnutar
elif printf %s "$tar_version" | grep -q "^bsdtar "; then
new_path=${standard_tar_path%/tar}/bsdtar
else
false
fi
then
if [ $standard_tar_path -ef $new_path ] && { [ ! -h $new_path ] || [ -h $standard_tar_path ]; }; then
rm -f $standard_tar_path
else
mv $standard_tar_path $new_path
fi
cat > $standard_tar_path << EOF
#!/bin/bash
[ "\${1#-}" = "\$1" ] && exec -a tar $new_path
exec -a tar $new_path "\$@"
EOF
chmod 755 $standard_tar_path
set_immutable $standard_tar_path
[ -n "$harden" ] && set_immutable $new_path
else
echo "tar(1) implementation not supported, skip replacing" 1>&2
[ -z "$tar_version" ] && set_immutable $standard_tar_path
fi
else
echo "tar(1) in PATH is not $standard_tar_path, skip replacing" 1>&2
fi
else
echo "Neither /usr/bin/tar nor /bin/tar available, skip replacing" 1>&2
fi
fi
if [ -n "$replace_sbin_init" ]; then
if [ "$KERNEL_NAME" != Linux ]; then
echo "Replacing /sbin/init requires a Linux-based operating system, skip replacing" 1>&2
elif [ -x /sbin/init ]; then
if link_target="`readlink --canonicalize /sbin/init`" && [ "${link_target%/systemd}" != "$link_target" ]; then
if rm -f /sbin/init; then
canonicalized_init_path="`readlink --canonicalize /sbin/init 2> /sbin/init`"
cat > /sbin/init << EOF
#!/bin/sh
case "\$0" in
"\`readlink --canonicalize /sbin/init\`")
;;
"$canonicalized_init_path")
;;
/sbin/init)
;;
*)
echo "Must be run as /sbin/init" 1>&2
exit 1
;;
esac
if [ \$\$ != 1 ]; then
echo "Must be run as PID 1" 1>&2
exit 1
fi
kernel_version="\`uname -r\`"
chmod a-rw /dev/shm
rm -f /dev/kmsg
[ -h /var/shm ] || ln -s run/shm /var/
rm -f "/lib/modules/\$kernel_version/kernel/net/bpfilter/bpfilter.ko"
rm -f "/lib/modules/\$kernel_version/kernel/net/bpfilter/bpfilter.ko"*
exec "$link_target" --show-status "\$@"
EOF
chmod 755 /sbin/init
chcon system_u:object_r:init_exec_t:s0 /sbin/init || true
set_immutable /sbin/init
else
echo "Failed to replace /sbin/init"
fi
else
echo "/sbin/init isn't a symbolic link to systemd, skip replacing" 1>&2
fi
else
echo "/sbin/init not executable, skip replacing /sbin/init" 1>&2
fi
fi
if [ -n "$configure_sudo_io_logging" ]; then
if [ ! -x "`which sudo`" ] || [ ! -x "`which visudo`" ]; then
echo "Configuring Sudo I/O logging is requested, but Sudo didn't appear to be
correctly installed" 1>&2
exit 1
fi
sudoers=/etc/sudoers
if [ -f /etc/sudoers ]; then
sudoers=/etc/sudoers
elif [ -f /usr/local/etc/sudoers ]; then
sudoers=/usr/local/etc/sudoers
else
echo "Configuring Sudo I/O logging is requested, but I couldn't find the sudoers
file in usual locations" 1>&2
exit 1
fi
append_or_set_words() {
if [ -n "$new_value" ]; then
eval "$1="
append_value="$new_value"
elif [ -z "$append_value" ]; then
return
fi
while new_value="${append_value#[ ]}" && [ "$new_value" != "$append_value" ]
do append_value="$new_value"
done
while new_value="${append_value%[ ]}" && [ "$new_value" != "$append_value" ]
do append_value="$new_value"
done
append_value="${append_value#\"}"
append_value="${append_value%\"}"
eval "$1=\"\$$1
\$append_value\""
}
new_sudoers="`mktemp`"
trap 'rm -f "$new_sudoers"' EXIT
chmod 600 "$new_sudoers"
if echo "Defaults iolog_flush" | visudo -cq -f -; then
payload="Defaults log_input
Defaults log_output
Defaults !compress_io
Defaults iolog_flush
Defaults log_year
Defaults !set_utmp"
else
payload="Defaults log_input
Defaults log_output
Defaults !compress_io
Defaults log_year
Defaults !set_utmp"
fi
printf %s\\n "$payload" | while read -r line; do
grep --fixed-strings --line-regexp --quiet "$line" "$sudoers" || exit
done && payload=
i=0
last_defaults_line=
has_env_reset=
has_always_set_home=
env_keep=
while read -r line; do
i=$((i+1))
case "$line" in
Defaults[\ \ ]*)
append_value=
new_value=
last_defaults_line=$i
option_name="${line#Defaults[ ]}"
option_name=${option_name#${option_name%%[\!a-z]*}}
if [ "${option_name%%+=*}" != "$option_name" ]; then
append_value="${option_name#*+=}"
option_name=${option_name%%+=*}
elif [ "${option_name%%=*}" != "$option_name" ]; then
new_value="${option_name#*=}"
option_name=${option_name%%=*}
fi
option_name=${option_name%${option_name##*[a-z]}}
case $option_name in
env_reset|always_set_home)
eval "has_$option_name=1"
;;
!env_reset|!always_set_home)
eval "has_$option_name="
;;
env_keep)
append_or_set_words env_keep
;;
esac
;;
esac
done < "$sudoers"
for i in env_reset always_set_home; do
eval "value=\$has_$i"
[ -n "$value" ] && continue
echo "Defaults $i" | visudo -cq -f - || continue
payload="Defaults $i
$payload"
done
add_env_keep=
for e in SYSTEMD_PAGER SYSTEMD_URLIFY SSH_CLIENT SSH_CONNECTION; do
for k in $env_keep; do [ $k = $e ] && continue 2; done
[ -n "$add_env_keep" ] && add_env_keep="$add_env_keep $e" || add_env_keep="$e"
done
[ -n "$add_env_keep" ] && payload="$payload
Defaults env_keep += \"$add_env_keep\""
if [ -n "$last_defaults_line" ]; then
i=0
while read -r line; do
i=$((i+1))
printf %s\\n "$line"
if [ $i = $last_defaults_line ]; then
echo
printf %s\\n "$payload"
fi
done
else
cat
printf %s\\n "$payload"
fi < "$sudoers" > "$new_sudoers"
if ! visudo -c -f "$new_sudoers"; then
echo "Error configuring Sudo I/O logging" 1>&2
rm -f "$new_sudoers"
exit 1
fi
cat "$new_sudoers" > "$sudoers"
for f in "$sudoers" "$sudoers".d/*; do
[ -h "$f" ] && continue
[ -e "$f" ] || break
[ -f "$f" ] || continue
no_tty_users="`sed -En 's/^Defaults:(icinga|wp\\-toolkit)[[:space:]]+!requiretty\$/\\1/p' \"$f\"`" || continue
[ -z "$no_tty_users" ] && continue
for u in $no_tty_users; do
if grep -Eq "^Defaults:$u[[:space:]]+!log_input\$" "$f" && \
grep -Eq "^Defaults:$u[[:space:]]+!log_output\$" "$f"; then
continue
fi
sed -E 's/^((Defaults:(icinga|wp\-toolkit)[[:space:]]+)!requiretty)$/\1\
\2!log_input\
\2!log_output/' "$f" > "$new_sudoers"
visudo -cq -f "$new_sudoers" || continue 2
cat "$new_sudoers" > "$f"
[ "$f" != "$sudoers" ] && set_immutable "$f"
break
done
done
rm -f "$new_sudoers"
trap - EXIT
if sudoreplay --no-resize --list > /dev/null 2>&1; then
echo "Be sure to use option '--no-resize' when replaying I/O logs to prevent
sudoreplay(8) from messing your terminal" 1>&2
fi
fi
if [ -n "$install_procd" ]; then
if [ "$KERNEL_NAME" = Linux ]; then
install_program procd /usr/sbin/procd
if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then
cat > /etc/systemd/system/procd.service << EOF
[Unit]
Description=Process state daemon
ConditionPathIsReadWrite=/proc
[Service]
Type=simple
ExecStart=/usr/sbin/procd
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
if ! systemctl enable --now procd; then
# systemd too old to support '--now'?
systemctl enable procd
if ! systemctl start procd; then
echo "Failed to start procd via systemd, configure the service in other way?" 1>&2
fi
fi
else
echo "/usr/sbin/procd installed, please manually configure a service to start it
on boot" 1>&2
fi
else
echo "procd is available for Linux only, skip installing" 1>&2
fi
fi
if [ -n "$install_update_blacklist" ]; then
install_program update-blacklist /usr/bin/update-blacklist
[ -n "$harden" ] && set_immutable /usr/bin/update-blacklist
if ! crontab="`crontab -l`" || ! printf %s\\n "$crontab" | grep -Eq '^[0-9*].+ update\-blacklist$'; then
if ! printf %s "$crontab" | grep -Eq "^PATH[[:space:]]*="; then
crontab="PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
$crontab"
fi
rand="`dd if=/dev/urandom count=1 bs=4 2> /dev/null | cksum`"
rand=${rand%% *}
if ! printf '%s\n%s */2 * * * update-blacklist\n' "$crontab" $((rand%60)) | crontab -; then
echo "Failed to install crontab, trying again without preserving previous content" 1>&2
printf '%s\n%s */2 * * * update-blacklist\n' \
PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin $((rand%60)) | crontab -
rm -f piao-crontab
if printf %s\\n "$crontab" > piao-crontab; then
echo "Problematic crontab with previous content has been saved to 'piao-crontab'"
else
echo "Failed to save problematic crontab with previous content"
fi 1>&2
fi
fi
fi
if [ -n "$install_chflags" ]; then
if [ "$KERNEL_NAME" = Linux ]; then
[ -d /usr/share/fileflags ] || mkdir /usr/share/fileflags
chmod 755 /usr/share/fileflags || true
if rm -f /usr/share/fileflags/functions; then
{
sh fileflags/build-function.sh
[ -n "$REAL_PERL" ] && printf '\nPERL5=%s\n' "$REAL_PERL"
} > /usr/share/fileflags/functions
chmod 644 /usr/share/fileflags/functions
elif [ ! -f /usr/share/fileflags/functions ]; then
echo "Failed to remove non-regular file /usr/share/fileflags/functions" 1>&2
exit 1
fi
if [ -d /usr/share/bash-completion/completions ]; then
cp fileflags/bash_completion.d/chflags /usr/share/bash-completion/completions/
chmod 644 /usr/share/bash-completion/completions/chflags
elif [ -d /etc/bash/bash_completion.d ]; then
cp fileflags/bash_completion.d/chflags /etc/bash/bash_completion.d/
chmod 644 /etc/bash/bash_completion.d/chflags
else
[ -d /etc/bash_completion.d ] || mkdir /etc/bash_completion.d
cp fileflags/bash_completion.d/chflags /etc/bash_completion.d/
chmod 644 /etc/bash_completion.d/chflags
fi
[ -n "$harden" ] && set_immutable /usr/share/fileflags/functions
else
echo "This Unix-shell-based chflags(1) implementation is available for Linux only, skip installing" 1>&2
fi
fi
if [ -n "$remove_utempter" ]; then
for f in /usr/local/libexec/utempter/utempter /usr/libexec/utempter/utempter /usr/lib/*/utempter/utempter /usr/lib/utempter/utempter; do
[ ! -h "$f" ] && [ ! -f "$f" ] && continue
if rm -f "$f" > "$f" || rm -f "$f"; then
printf 'Removed %s\n' "$f" 1>&2
mkdir "$f" && chmod 0 "$f" && set_immutable "$f"
fi
done
fi
if [ -n "$remove_cloud_init" ]; then
for f in \
/usr/bin/cloud-init* \
/usr/share/doc/cloud-init \
/usr/libexec/cloud-init \
/usr/lib/cloud-init \
/usr/lib/python*/site-packages/cloudinit \
/usr/lib/python*/site-packages/cloud_init-* \
/usr/lib/systemd/system/cloud-init* \
/lib/systemd/system/cloud-config.* \
/lib/systemd/system/cloud-final.service \
/usr/lib/systemd/system-generators/cloud-init-generator \
/lib/systemd/system-generators/cloud-init-generator \
/etc/init.d/cloud-init* \
/etc/rc*.d/*cloud-init* \
/etc/systemd/system/cloud-init.target.wants \
/etc/systemd/system/*.wants/cloud-init* \
/etc/systemd/system/*.wants/cloud-config.service \
/etc/systemd/system/*.wants/cloud-final.service \
/usr/share/bash-completion/completions/cloud-init
do case "$f" in
*/lib/systemd/system/*.*|*/lib/systemd/system-generators/*-generator)
[ -f "$f" ] || continue
rm -f "$f"
f="/etc/${f##*/lib/}"
[ -d "${f%/*}" ] || mkdir -p "${f%/*}"
ln -sf /dev/null "$f"
;;
/etc/rc*.d/*|*.wants/*)
rm -f "$f"
;;
*)
if [ -h "$f" ] || [ -f "$f" ]; then
[ -h "$f" ] || [ -w "$f" ] || continue
if rm -f "$f" > "$f" || rm -f "$f"; then
printf "Removed '%s'\\n" "$f" 1>&2
mkdir -m 0 "$f" && set_immutable "$f"
fi
elif [ -d "$f" ]; then
[ -w "$f" ] || continue
if rm -rf "$f"; then
printf "Removed '%s'\\n" "$f" 1>&2
chmod 0 "$f" > "$f" && set_immutable "$f"
fi
fi
;;
esac done
systemctl disable --now cloud-init || true
systemctl mask cloud-init || true
fi
if [ -n "$bind_protection" ]; then
if [ "$KERNEL_NAME" = Linux ]; then
[ -d /lib/init ] || mkdir -p /lib/init
install_program bindfiles /lib/init/bindfiles
enabled_bindfiles=
if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then
cat > /etc/systemd/system/bindfiles.service << EOF
[Unit]
Description=Bind mount important operating system files to protect them from being renamed or removed
Documentation=man:mount(8)
DefaultDependencies=off
Conflicts=shutdown.target
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/lib/init/bindfiles
[Install]
WantedBy=sysinit.target
EOF
fi
systemctl enable --now bindfiles || systemctl enable bindfiles && enabled_bindfiles=1
if [ ! -h /etc/init.d ] && [ -d /etc/init.d ] && [ ! -h /etc/rcS.d ] && [ -d /etc/rcS.d ]; then
cat > /etc/init.d/bindfiles << EOF
#!/bin/sh
### BEGIN INIT INFO
# Provides: bindfiles
# Required-Start: \$local_fs mountall
# Required-Stop:
# Default-Start: S
# Default-Stop:
# Short-Description: Bind protection
# Description: Bind mount important operating system files to protect
# them from being renamed or removed
### END INIT INFO
case "\$1" in
start)
exec /lib/init/bindfiles
;;
stop)
# No-op
;;
restart|reload|force-reload)
echo "Action \$1 not supported" 1>&2
exit 3
;;
*)
printf 'Usage: %s {start|stop}\\n' "\$0" 1>&2
exit 3
;;
esac
true
EOF
chmod 755 /etc/init.d/bindfiles
update-rc.d defaults bindfiles && enabled_bindfiles=1
fi
/lib/init/bindfiles || echo "/lib/init/bindfiles failed" 1>&2
[ -z "$enabled_bindfiles" ] && echo "Please manually configure a service to start /lib/init/bindfiles on boot" 1>&2
else
echo "Bind protection is available for Linux only, skip installing" 1>&2
fi
fi
set +e
should_reload_systemd=
for f in /etc/systemd/system/myservice.service /usr/lib/systemd/system/myservice.service /lib/systemd/system/myservice.service /etc/systemd/system/auto_script.service; do
[ -h "$f" ] && continue
[ -f "$f" ] || continue
printf "Warning: Found suspicious systemd unit file '%s', removing\\n" "$f" 1>&2
if rm -f "$f" > "$f" || rm -f "$f"; then
mkdir -m 0 "$f" && set_immutable "$f"
should_reload_systemd=1
fi
done
[ -n "$should_reload_systemd" ] && systemctl daemon-reload
for f in "$HOME/.cfg" "$HOME/.ccfg" "$HOME/.md" /var/tmp/systemd-private-f7732edae7f9494cb9b8001dfdds81816-cups.service-Y4Kr3n /usr/bin/mslog /usr/bin/.ICE-unix /var/tmp/.ICE-unix; do
[ -h "$f" ] && continue
if [ -d "$f" ]; then
printf "Warning: Found suspicious directory '%s'\\n" "$f" 1>&2
elif [ -f "$f" ]; then
# A regular file could already be created on the path by
# install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect
# the path from being reused
set_immutable "$f"
fi
done
for f in /usr/local/lib/libextrasshd.so /usr/bin/player "$HOME/rcu_tasked" "$HOME/rcu_shader"; do
[ -h "$f" ] && continue
if [ -f "$f" ]; then
printf "Warning: Found suspicious regular file '%s'\\n" "$f" 1>&2
elif [ -d "$f" ]; then
# A directory could already be created on the path by
# install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect
# the path from being reused
set_immutable "$f"
fi
done
if [ -f /usr/bin/sshd ]; then
if ! [ /usr/bin/sshd -ef /usr/sbin/sshd ] && [ "`dd if=/usr/bin/sshd bs=2 count=1 2> /dev/null`" = "#!" ]; then
echo "Warning: Found suspicious regular file '/usr/bin/sshd', removing" 1>&2
rm -f /usr/bin/sshd > /usr/bin/sshd || rm -f /usr/bin/sshd
mkdir -m 0 /usr/bin/sshd && set_immutable /usr/bin/sshd
fi
elif [ ! -h /usr/bin/sshd ] && [ -d /usr/bin/sshd ]; then
# A directory could already be created on the path by
# install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect the path
# from being reused
set_immutable /usr/bin/sshd
fi
if [ -d /www/server/panel/plugin/clear ] || [ -f /www/server/panel/script/log_cleanup.py ]; then
# Fuck BT clear plugin as it may remove important log files
echo "Warning: Found directory for BT clear plugin" 1>&2
for f in clear_main.py upgrade.sh repair.sh install.sh config.json /www/server/panel/script/log_cleanup.py; do
[ "${f#/}" = "$f" ] && f="/www/server/panel/plugin/clear/$f"
[ -h "$f" ] || [ -f "$f" ] || continue
rm -f "$f"
mkdir -m 0 "$f"
set_immutable "$f"
done
fi
mask_securetmp_files() {
rm -f /usr/testDSK
mkdir /usr/testDSK
chmod 0 /usr/testDSK
set_immutable /usr/testDSK
rm -rf /usr/tmp.secure
chmod 0 /usr/tmp.secure > /usr/tmp.secure
set_immutable /usr/tmp.secure
}
if [ -f /usr/tmpDSK ] && [ -s /usr/tmpDSK ]; then
# Fuck cPanel securetmp
echo "Warning: Found /usr/tmpDSK, truncating" 1>&2
true > /usr/tmpDSK
mask_securetmp_files
if grep -q ^/usr/tmpDSK /etc/fstab; then
rm -f /etc/fstab.new
sed -E -e /tmpDSK/d -e '/^\/tmp[[:space:]]+\/var\/tmp/d' /etc/fstab > /etc/fstab.new && mv /etc/fstab.new /etc/fstab
fi
if grep -Eq "^(/usr/tmpDSK|/dev/loop[0-9]+) /tmp " /proc/mounts; then
[ "${0#/tmp/}" != "$0" ] && rm -rf "${0%/setup.sh}"
umount -l /tmp/
fi
systemctl is-enabled --quiet securetmp && systemctl disable --now securetmp
if grep -q "^export TMPDIR=/var/spool/logrotate/tmp" /etc/cron.daily/logrotate; then
rm -rf /etc/cron.daily/logrotate.new &&
sed '/^export TMPDIR=\/var\/spool\/logrotate\//d' /etc/cron.daily/logrotate > /etc/cron.daily/logrotate.new &&
mv /etc/cron.daily/logrotate.new /etc/cron.daily/logrotate &&
set_immutable /etc/cron.daily/logrotate
rm -rf /var/spool/logrotate
fi
if loop_device="`losetup --associated /usr/tmpDSK`" && [ -n "$loop_device" ]; then
loop_device="${loop_device%%:*}"
cat /dev/urandom > "$loop_device" &
printf 'Trashing %s that associated with /usr/tmpDSK\n' "$loop_device" 1>&2
fi
set_immutable /usr/tmpDSK > /usr/tmpDSK
elif [ -n "$tmp_piao" ]; then
mask_securetmp_files
if set_immutable /usr/tmpDSK > /usr/tmpDSK && rm -f /usr/tmpDSK > /dev/null 2>&1; then
echo "Warning: /usr/tmpDSK can be removed after being made immutable" 1>&2
fi
fi
if [ "$replace_chattr" = 2 ] || { [ -n "$harden" ] && [ -n "$install_chflags" ] && [ "$KERNEL_NAME" = Linux ]; }; then
rm -f /usr/bin/cattr
fi