#!/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" }