xbps-triggers: make system-accounts behave in altroots

The system-accounts XBPS trigger originally used getent(1) to check for
existing users or groups before attempting to create those entities
defined by package templates. When invoked on an alternate root (for
example, with `xbps-install -r /path/to/root`), this is incorrect
because getent always looks for entries in host databases.

There is no need to check for existing accounts before attempting to
invoke useradd(8) or groupadd(8) because these programs will fail with a
specific error code when the creation conflicts with an existing entity.
The modified hook just attempts to create users and groups from the
start, detecting the "already exists" return code and doing the right
thing in that case (nothing further for groups, but modifying existing
user entities).

When the trigger acts on the system root, the useradd/groupadd/usermod
invocations are aware of remote NIS or LDAP directories and should
behave in these environments. In particular, the tools will not attempt
to create entities defined in remote directories.

In an alternate root, it isn't really appropriate to consider remote
directories, because there is no guarantee that the alternate root will
be using those directories. When the trigger acts on an alternate root,
it uses the `--prefix|-P` argument to useradd/groupadd/usermod, which
disregards NIS and LDAP and acts only on local files in the given
prefix. Most importantly, this ensures that the hook will not attempt to
create users or groups on the host when acting on an alternate root.

Closes: #24812
This commit is contained in:
Andrew J. Hesford 2020-09-10 11:39:44 -04:00
parent 8b22c9e453
commit 63283d403c
2 changed files with 94 additions and 78 deletions

View File

@ -16,29 +16,45 @@ UPDATE="$5"
export PATH="usr/sbin:usr/bin:/usr/sbin:/usr/bin:/sbin:/bin" export PATH="usr/sbin:usr/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# Determine whether useradd/groupadd/usermod need a prefix argument
if [ "$(readlink -f . 2>/dev/null || echo .)" != "/" ]; then
prefix="-P ."
else
prefix=
fi
# show_acct_details <username> <description> <homedir> <shell> [groups]
show_acct_details() {
echo " Account: $1"
echo " Description: '$2'"
echo " Homedir: '$3'"
echo " Shell: '$4'"
[ -n "$5" ] && echo " Additional groups: '$5'"
}
group_add() { group_add() {
local _grname _gid use_gid local _pretty_grname _grname _gid _prefix
if ! command -v groupadd >/dev/null 2>&1; then
echo "WARNING: cannot create $1 system group (missing groupadd)"
echo "The following group must be created manually: $1"
return
fi
_grname="${1%:*}" _grname="${1%:*}"
_gid="${1#*:}" _gid="${1##*:}"
if [ "${_gid}" != "${_grname}" ]; then [ "${_grname}" = "${_gid}" ] && _gid=
use_gid="gid ${_gid}"
fi
if ! getent group ${_grname} >/dev/null; then _pretty_grname="${_grname}${_gid:+ (gid: ${_gid})}"
if [ -n "$use_gid" ]; then
groupadd -r ${_grname} -g ${_gid} >/dev/null 2>&1 groupadd ${prefix} -r ${_grname} ${_gid:+-g ${_gid}} >/dev/null 2>&1
else
groupadd -r ${_grname} >/dev/null 2>&1 case $? in
fi 0) echo "Created ${_pretty_grname} system group." ;;
if [ $? -eq 0 ]; then 9) ;;
echo "Created ${_grname} ($use_gid) system group." *) echo "ERROR: failed to create system group ${_pretty_grname}!"; exit 1;;
else esac
echo "Failed to create ${_grname} ($use_gid) system group!"
exit 1
fi
fi
} }
case "$ACTION" in case "$ACTION" in
@ -46,108 +62,108 @@ targets)
echo "post-install pre-remove" echo "post-install pre-remove"
;; ;;
run) run)
if [ -z "$system_accounts" -a -z "$system_groups" ]; then [ -z "$system_accounts" -a -z "$system_groups" ] && exit 0
exit 0
if command -v useradd >/dev/null 2>&1; then
USERADD="useradd ${prefix}"
fi fi
if [ -x sbin/useradd -o -x bin/useradd ]; then if command -v usermod >/dev/null 2>&1; then
USERADD=1 USERMOD="usermod ${prefix}"
fi
if [ -x sbin/usermod -o -x bin/usermod ]; then
USERMOD=1
fi
if [ -x sbin/groupadd -o -x bin/groupadd ]; then
GROUPADD=1
fi
if [ -x bin/getent -o -x sbin/getent ]; then
GETENT=1
fi
if [ -x bin/passwd -o -x sbin/passwd ]; then
PASSWD=1
fi fi
case "$TARGET" in case "$TARGET" in
post-install) post-install)
# System groups required by a package. # System groups required by a package.
for grp in ${system_groups}; do for grp in ${system_groups}; do
if [ -z "$GROUPADD" -a -z "$GETENT" ]; then
echo "WARNING: cannot create ${grp} system group (missing groupadd/getent)"
echo "The following group must be created manually: $grp"
continue
fi
group_add $grp group_add $grp
done done
# System user/group required by a package. # System user/group required by a package.
for acct in ${system_accounts}; do for acct in ${system_accounts}; do
_uname="${acct%:*}" _uname="${acct%:*}"
_uid="${acct#*:}" _uid="${acct##*:}"
[ "${_uname}" = "${_uid}" ] && _uid=
eval homedir="\$${_uname}_homedir" eval homedir="\$${_uname}_homedir"
eval shell="\$${_uname}_shell" eval shell="\$${_uname}_shell"
eval descr="\$${_uname}_descr" eval descr="\$${_uname}_descr"
eval groups="\$${_uname}_groups" eval groups="\$${_uname}_groups"
eval pgroup="\$${_uname}_pgroup" eval pgroup="\$${_uname}_pgroup"
[ -z "$homedir" ] && homedir="/var/empty" [ -z "$homedir" ] && homedir="/var/empty"
[ -z "$shell" ] && shell="/sbin/nologin" [ -z "$shell" ] && shell="/sbin/nologin"
[ -z "$descr" ] && descr="${_uname} unprivileged user" [ -z "$descr" ] && descr="${_uname} unprivileged user"
[ -n "$groups" ] && user_groups="-G $groups" [ -n "$groups" ] && user_groups="-G $groups"
[ "${_uid}" != "${_uname}" ] && if [ -n "${_uid}" ]; then
use_id="-u ${_uid} -g ${pgroup:-${_uid}}" use_id="-u ${_uid} -g ${pgroup:-${_uid}}"
_pretty_uname="${_uname} (uid: ${_uid})"
else
use_id="-g ${pgroup:-${_uname}}"
_pretty_uname="${_uname}"
fi
if [ -z "$USERADD" -a -z "$GETENT" -a -z "$PASSWD" ]; then if [ -z "$USERADD" -o -z "$USERMOD" ]; then
echo "WARNING: cannot create ${acct} system user/group (missing useradd/getent/passwd)" echo "WARNING: cannot create ${_uname} system account (missing useradd or usermod)"
echo "The following system account must be created:" echo "The following system account must be created:"
echo " Account: ${uname:-${_uid}} (uid: '${_uid}')" show_acct_details "${_pretty_uname}" "${descr}" "${homedir}" "${shell}" "${groups}"
echo " Description: '${descr}'"
echo " Homedir: '${homedir}'"
echo " Shell: '${shell}'"
echo " Additional groups: '${groups}'"
continue continue
fi fi
group_add ${pgroup:-${acct}} group_add ${pgroup:-${acct}}
if ! getent passwd ${_uname} >/dev/null; then ${USERADD} -c "${descr}" -d "${homedir}" \
useradd -c "$descr" -d "$homedir" -s "$shell" ${user_groups} \ ${use_id} ${pgroup:+-N} -s "${shell}" \
${pgroup:+-N} ${use_id:=-g ${pgroup:-${_uname}}} -r ${_uname} && \ ${user_groups} -r ${_uname} >/dev/null 2>&1
passwd -l ${_uname} >/dev/null 2>&1
if [ $? -eq 0 ]; then case $? in
echo "Created ${_uname} (${_uid}) system user." 0)
else echo "Created ${_pretty_uname} system user."
echo "Failed to create ${acct} system user!" ${USERMOD} -L ${_uname} >/dev/null 2>&1
exit 1 if [ $? -ne 0 ]; then
echo "WARNING: unable to lock password for ${_uname} system account"
fi fi
;;
9)
${USERMOD} -c "${descr}" -d "${homedir}" \
-s "${shell}" -g "${pgroup:-${_uname}}" \
${user_groups} ${_uname} >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Updated ${_uname} system user."
else else
if [ -z "$USERMOD" ]; then echo "WARNING: unable to modify ${_uname} system account"
echo "WARNING: cannot update ${acct} system user/group (missing usermod)" echo "Please verify that account is compatible with these settings:"
show_acct_details "${_pretty_uname}" \
"${descr}" "${homedir}" "${shell}" "${groups}"
continue continue
fi fi
usermod -c "${descr}" -d "${homedir}" -s "${shell}" ${user_groups} \ ;;
-g "${pgroup:-${_uname}}" ${_uname} >/dev/null 2>&1 *)
if [ $? -eq 0 ]; then echo "ERROR: failed to create system user ${_pretty_uname}!"
echo "Updated ${_uname} (${_uid}) system user."
else
echo "Failed to update ${acct} system user!"
exit 1 exit 1
fi ;;
fi esac
done done
;; ;;
pre-remove) pre-remove)
if [ "$UPDATE" = "no" ]; then if [ "$UPDATE" = "no" ]; then
for acct in ${system_accounts}; do for acct in ${system_accounts}; do
_uname="${acct%:*}" _uname="${acct%:*}"
_uid="${acct#*:}"
comment="$(getent passwd "${_uname}" |cut -d: -f5 |head -n1) - for uninstalled package ${PKGNAME}" comment="$( (getent passwd "${_uname}" | cut -d: -f5 | head -n1) 2>/dev/null )"
comment="${comment:-unprivileged user} - for uninstalled package ${PKGNAME}"
if [ -z "$USERMOD" ]; then if [ -z "$USERMOD" ]; then
echo "WARNING: cannot disable ${acct} system user/group (missing usermod)" echo "WARNING: cannot disable ${_uname} system user (missing usermod)"
continue continue
fi fi
usermod -L -d /var/empty -s /bin/false -c "${comment}" ${_uname} >/dev/null 2>&1
${USERMOD} -L -d /var/empty -s /bin/false \
-c "${comment}" ${_uname} >/dev/null 2>&1
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Disabled ${_uname} (${_uid}) system user/group." echo "Disabled ${_uname} system user."
fi fi
done done
fi fi

View File

@ -1,7 +1,7 @@
# Template file for 'xbps-triggers' # Template file for 'xbps-triggers'
pkgname=xbps-triggers pkgname=xbps-triggers
version=0.116 version=0.117
revision=2 revision=1
bootstrap=yes bootstrap=yes
short_desc="XBPS triggers for Void Linux" short_desc="XBPS triggers for Void Linux"
maintainer="Enno Boland <gottox@voidlinux.org>" maintainer="Enno Boland <gottox@voidlinux.org>"