iopsys-feed/usermngr/files/etc/init.d/users
Mohd Husaam Mehdi 7e87028731 usermngr: add support for USERMNGR_SECURITY_HARDENING
* this compile time flag will set up faildelay, lock out upon
  6 failed attempts during login via shell or console and enable
  password strength checks when changing password
* this will set openssh usePam option
* openssh and shadow-utils with PAM backend need to be selected for
  this compile time flag to work
2025-07-23 13:12:31 +05:30

170 lines
4.4 KiB
Bash
Executable file

#!/bin/sh /etc/rc.common
START=11
STOP=90
USE_PROCD=1
PROG=/usr/sbin/usermngr
# List of required .so files
REQUIRED_MODULES="
/usr/lib/security/pam_faildelay.so
/usr/lib/security/pam_faillock.so
/usr/lib/security/pam_unix.so
/usr/lib/security/pam_deny.so
/usr/lib/security/pam_permit.so
/usr/lib/security/pam_passwdqc.so
"
check_required_modules() {
for mod in $REQUIRED_MODULES; do
if [ ! -f "$mod" ]; then
logger -p err -t usermngr "ERROR: Cannot setup security policy, missing PAM module: $mod"
return 1
fi
done
return 0
}
write_line() {
local filepath="$1"
local line="$2"
echo "$line" >> "$filepath"
}
compare_and_replace() {
local src dst
src="$1"
dst="$2"
if [ ! -f "$dst" ] || ! cmp -s "$src" "$dst"; then
cp "$src" "$dst"
logger -t pam_policy_setup "Updated $dst"
fi
}
update_auth() {
# Write /etc/pam.d/common-auth
local tmp_file pam_file
tmp_file="/tmp/common-auth"
pam_file="/etc/pam.d/common-auth"
rm -f "$tmp_file"
touch "$tmp_file"
write_line "$tmp_file" "auth optional pam_faildelay.so delay=$faildelay_usec"
write_line "$tmp_file" "auth required pam_faillock.so preauth deny=$faillock_attempts even_deny_root unlock_time=$faillock_lockout_time"
write_line "$tmp_file" "auth sufficient pam_unix.so nullok_secure"
write_line "$tmp_file" "auth [default=die] pam_faillock.so authfail audit deny=$faillock_attempts even_deny_root unlock_time=$faillock_lockout_time"
write_line "$tmp_file" ""
write_line "$tmp_file" "auth requisite pam_deny.so"
write_line "$tmp_file" "auth required pam_permit.so"
compare_and_replace "$tmp_file" "$pam_file"
}
update_password() {
# Write /etc/pam.d/common-password
local tmp_file pam_file
tmp_file="/tmp/common-password"
pam_file="/etc/pam.d/common-password"
rm -f "$tmp_file"
touch "$tmp_file"
# for some reason setting to 8 makes passwdqc accept minimum 12 letter password with this configuration
# if we set it to 12 then we need atleast 16 characters and so on
# passphrase = 0 means no space separated words
# rest can be figured out from passwdqc man page
write_line "$tmp_file" "password requisite pam_passwdqc.so min=disabled,disabled,disabled,disabled,8 max=20 passphrase=0 retry=3 enforce=everyone"
write_line "$tmp_file" "password [success=1 default=ignore] pam_unix.so obscure sha512"
write_line "$tmp_file" ""
write_line "$tmp_file" "password requisite pam_deny.so"
write_line "$tmp_file" "password required pam_permit.so"
compare_and_replace "$tmp_file" "$pam_file"
}
update_account() {
# Write /etc/pam.d/common-account
local tmp_file pam_file
tmp_file="/tmp/common-account"
pam_file="/etc/pam.d/common-account"
rm -f "$tmp_file"
touch "$tmp_file"
write_line "$tmp_file" "account required pam_faillock.so"
write_line "$tmp_file" "account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so"
write_line "$tmp_file" ""
write_line "$tmp_file" "account requisite pam_deny.so"
write_line "$tmp_file" "account required pam_permit.so"
compare_and_replace "$tmp_file" "$pam_file"
}
handle_security_policy() {
local enable faildelay faillock_lockout_time faillock_attempts faildelay_usec
# Read UCI values
enable="$(uci -q get users.security_policy.enable)"
faildelay="$(uci -q get users.security_policy.fail_delay)"
faillock_lockout_time="$(uci -q get users.security_policy.faillock_lockout_time)"
faillock_attempts="$(uci -q get users.security_policy.faillock_attempts)"
# if it is not enabled we do not touch the pam files
[ "$enable" = "1" ] || return
# if any .so files are missing, then we cannot setup security
if ! check_required_modules; then
return
fi
[ -n "$faildelay" ] || faildelay=3
[ -n "$faillock_attempts" ] || faillock_attempts=6
[ -n "$faillock_lockout_time" ] || faillock_lockout_time=300
# Convert seconds to microseconds for pam_faildelay
faildelay_usec=$((faildelay * 1000000))
update_auth
update_account
update_password
}
start_service() {
local loglevel
loglevel="$(uci -q get users.users.loglevel)"
handle_security_policy
procd_open_instance usermngr
procd_set_param command $PROG
if [ -n "${loglevel}" ]; then
procd_append_param command -l "${loglevel}"
fi
procd_set_param respawn
procd_close_instance
}
reload_service() {
ret=$(ubus call service list '{"name":"users"}' | jsonfilter -qe '@.users.instances.usermngr.running')
if [ "$ret" != "true" ]; then
stop
start
else
ubus send usermngr.reload
fi
return 0
}
service_triggers()
{
procd_add_reload_trigger "users"
}