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 help
Enable this option to use PAM based faillock, passwdqc, faildelay for security hardening. 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 depends on USERMNGR_SECURITY_HARDENING
bool "Exposes vendor datamodel extensions for AuthenticationPolicy" bool "Expose vendor datamodel extensions."
default y default y
help help
Enable this option to expose TR181 vendor extensions for AuthenticationPolicy. Enable this option to expose TR181 vendor extensions.
config USERMNGR_VENDOR_PREFIX 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" string "Package specific datamodel Vendor Prefix for TR181 extensions"
default "" default ""

View file

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

View file

@ -16,10 +16,18 @@ REQUIRED_MODULES="
/usr/lib/security/pam_passwdqc.so /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() { check_required_modules() {
for mod in $REQUIRED_MODULES; do for mod in $REQUIRED_MODULES; do
if [ ! -f "$mod" ]; then 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 return 1
fi fi
done done
@ -41,7 +49,7 @@ compare_and_replace() {
if [ ! -f "$dst" ] || ! cmp -s "$src" "$dst"; then if [ ! -f "$dst" ] || ! cmp -s "$src" "$dst"; then
cp "$src" "$dst" cp "$src" "$dst"
logger -t pam_policy_setup "Updated $dst" log "Updated $dst"
fi fi
} }
@ -165,9 +173,87 @@ update_account() {
compare_and_replace "$tmp_file" "$pam_file" 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() { handle_security_policy() {
local auth_enabled enabled local auth_enabled enabled
config_load users
config_foreach update_password_expiry user
# Read UCI values # Read UCI values
auth_enabled="$(uci -q get users.users.auth_policy_enable || echo 0)" auth_enabled="$(uci -q get users.users.auth_policy_enable || echo 0)"
enabled="$(uci -q get users.authentication_policy.enabled || echo 0)" enabled="$(uci -q get users.authentication_policy.enabled || echo 0)"