usermngr: added password expiry extension

This commit is contained in:
Mohd Husaam Mehdi 2025-10-17 20:39:33 +05:30 committed by Vivek Kumar Dutta
parent 02ee448479
commit 95778a4262
No known key found for this signature in database
GPG key ID: 4E09F5AD8265FD4C
3 changed files with 97 additions and 10 deletions

View file

@ -6,15 +6,15 @@ config USERMNGR_SECURITY_HARDENING
help
Enable this option to use PAM based faillock, passwdqc, faildelay for security hardening.
config USERMNGR_ENABLE_AUTH_VENDOR_EXT
config USERMNGR_ENABLE_VENDOR_EXT
depends on USERMNGR_SECURITY_HARDENING
bool "Exposes vendor datamodel extensions for AuthenticationPolicy"
bool "Expose vendor datamodel extensions."
default y
help
Enable this option to expose TR181 vendor extensions for AuthenticationPolicy.
Enable this option to expose TR181 vendor extensions.
config USERMNGR_VENDOR_PREFIX
depends on USERMNGR_ENABLE_AUTH_VENDOR_EXT
depends on USERMNGR_ENABLE_VENDOR_EXT
string "Package specific datamodel Vendor Prefix for TR181 extensions"
default ""

View file

@ -5,13 +5,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=usermngr
PKG_VERSION:=1.4.5
PKG_VERSION:=1.4.6
LOCAL_DEV:=0
ifneq ($(LOCAL_DEV),1)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://dev.iopsys.eu/bbf/usermngr.git
PKG_SOURCE_VERSION:=1082342fda754d7219dbca7dec22d34ad4ff7f8e
PKG_SOURCE_VERSION:=416c49b53ed2fbbc983f404c65b8a8ce722152bd
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
PKG_MIRROR_HASH:=skip
endif
@ -36,6 +36,7 @@ define Package/usermngr
DEPENDS+=+@USERMNGR_SECURITY_HARDENING:BUSYBOX_CONFIG_PAM
DEPENDS+=+USERMNGR_SECURITY_HARDENING:linux-pam
DEPENDS+=+USERMNGR_SECURITY_HARDENING:passwdqc
DEPENDS+=+USERMNGR_ENABLE_VENDOR_EXT:shadow-chage
TITLE:=Package to add Device.Users. datamodel support
endef
@ -57,8 +58,8 @@ ifeq ($(CONFIG_USERMNGR_SECURITY_HARDENING),y)
MAKE_FLAGS += USERMNGR_SECURITY_HARDENING=y
endif
ifeq ($(CONFIG_USERMNGR_ENABLE_AUTH_VENDOR_EXT),y)
MAKE_FLAGS += USERMNGR_ENABLE_AUTH_VENDOR_EXT=y
ifeq ($(CONFIG_USERMNGR_ENABLE_VENDOR_EXT),y)
MAKE_FLAGS += USERMNGR_ENABLE_VENDOR_EXT=y
endif
ifeq ($(CONFIG_USERMNGR_VENDOR_PREFIX),"")

View file

@ -16,10 +16,18 @@ REQUIRED_MODULES="
/usr/lib/security/pam_passwdqc.so
"
log() {
echo "$*" | logger -t user.init -p info
}
log_err() {
echo "$*" | logger -t user.init -p err
}
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"
log_err "ERROR: Cannot setup security policy, missing PAM module: $mod"
return 1
fi
done
@ -41,7 +49,7 @@ compare_and_replace() {
if [ ! -f "$dst" ] || ! cmp -s "$src" "$dst"; then
cp "$src" "$dst"
logger -t pam_policy_setup "Updated $dst"
log "Updated $dst"
fi
}
@ -165,9 +173,87 @@ update_account() {
compare_and_replace "$tmp_file" "$pam_file"
}
update_password_expiry() {
local user password_expiry shadow_entry lastchg_days lastchg_epoch expiry_epoch expiry_days max_days current_max_days
user="$1"
# Fetch expiry (in yyyy-mm-dd format) from UCI
config_get password_expiry "$user" password_expiry
# Get /etc/shadow entry for the user
shadow_entry="$(grep "^${user}:" /etc/shadow 2>/dev/null)"
if [ -z "$shadow_entry" ]; then
log_err "No shadow entry found for $user, skipping"
return
fi
current_max_days="$(echo "$shadow_entry" | cut -d: -f5)"
# Handle infinite lifetime (no expiry set)
if [ -z "$password_expiry" ]; then
if [ "$current_max_days" = "-1" ] || [ -z "$current_max_days" ]; then
log "Password for $user already set to never expire, no action needed"
else
chage -M -1 "$user"
log "Set password expiry for $user to never expire (previous max_days=$current_max_days)"
fi
return
fi
# option value is of the form 9999-12-31T23:59:59Z, so extract date
# because shadow/chage only allow us to specify expiry date
password_expiry="$(echo "$password_expiry" | cut -d 'T' -f 1)"
# Validate date format (yyyy-mm-dd)
if ! echo "$password_expiry" | grep -Eq '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'; then
log_err "Invalid password_expiry format for $user: '$password_expiry' (expected yyyy-mm-dd)"
return
fi
# Convert expiry date to epoch seconds
expiry_epoch="$(date -d "$password_expiry" +%s 2>/dev/null)"
if [ -z "$expiry_epoch" ]; then
log_err "Failed to parse expiry date '$password_expiry' for $user"
return
fi
# Extract 'lastchg' field (3rd colon-separated field)
lastchg_days="$(echo "$shadow_entry" | cut -d: -f3)"
if [ -z "$lastchg_days" ] || [ "$lastchg_days" = "0" ]; then
log_err "No valid last password change date for $user, skipping"
return
fi
# Convert lastchg_days to epoch seconds
lastchg_epoch=$(( lastchg_days * 86400 ))
# Compute difference in days between expiry and last change
expiry_days=$(( (expiry_epoch - lastchg_epoch) / 86400 ))
# round up to full day, because of this expiry_days can never be 0, but I think that is a non-issue
expiry_days=$(( expiry_days + 1 ))
if [ "$expiry_days" -lt 0 ]; then
log_err "Expiry date for $user ($password_expiry) is before last password change, skipping"
return
fi
max_days="$expiry_days"
# Apply update if changed
if [ "$current_max_days" != "$max_days" ]; then
chage -M "$max_days" "$user"
log "Updated max_days for $user to $max_days (expiry=$password_expiry, lastchg_days=$lastchg_days)"
else
log "max_days for $user already set to $max_days (expiry=$password_expiry), no change"
fi
}
handle_security_policy() {
local auth_enabled enabled
config_load users
config_foreach update_password_expiry user
# Read UCI values
auth_enabled="$(uci -q get users.users.auth_policy_enable || echo 0)"
enabled="$(uci -q get users.authentication_policy.enabled || echo 0)"