1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
#!/bin/sh
# Copyright (c) 2018 - 2022 Chris Cromer
# Released under the 2-clause BSD license.
#
# This is an implementation of the systemd-sysusers command
sysusersver=0.7.3
warninvalid() {
printf "sysusers: %s on line %d of '%s'\n" "${1:-ignoring invalid entry}" \
"${lineno}" "${file}"
: "$((error += 1))"
} >&2
add_group() {
# add_group <name> <id>
if [ "$2" = '-' ]; then
grep -q "^$1:" "$root/etc/group" || groupadd --prefix "$root" -r "$1"
elif ! grep -q "^$1:\|^[^:]*:[^:]*:$2:[^:]*$" "$root/etc/group"; then
groupadd --prefix "$root" -g "$2" "$1"
fi
}
add_user() {
# add_user <name> <uid> <gid> <gecos> <home> [locked]*
if ! id "$1" >/dev/null 2>&1; then
if [ "$2" = '-' ]; then
if [ "$3" = '-' ]; then
useradd --prefix "$root" -rc "$4" -g "$1" -d "$5" -s '/sbin/nologin' "$1"
else
useradd --prefix "$root" -rc "$4" -g "$3" -d "$5" -s '/sbin/nologin' "$1"
fi
else
useradd --prefix "$root" -rc "$4" -u "$2" -g "$3" -d "$5" -s '/sbin/nologin' "$1"
fi
passwd --prefix "$root" -l "$1" >/dev/null 2>&1
while [ $# -gt 5 ]; do
case "$6" in
locked) usermod --prefix "$root" -e 1 "$1" ;;
esac
shift
done
fi
}
update_login_defs() {
# update_login_defs <name> <id>
[ "$1" != '-' ] && warninvalid && return
min="${2%%-*}" max="${2#*-}"
[ "${max}" != "${max#*-}" ] && warninvalid && return
[ "${min}" -ge "${max}" ] && warninvalid "invalid range" && return
while read -r key val; do
case "${key}" in
SYS_UID_MAX) suid_max="${val}" ;;
SYS_GID_MAX) sgid_max="${val}" ;;
esac
done < "${root}/etc/login.defs"
[ "${min}" -lt "${suid_max}" ] && warninvalid "invalid range" && return
[ "${min}" -lt "${sgid_max}" ] && warninvalid "invalid range" && return
sed -e "/[GU]ID_MIN[[:space:]]\+/s/[^[:space:]]*$/${min}/" \
-e "/[GU]ID_MAX[[:space:]]\+/s/[^[:space:]]*$/${max}/" \
-i "${root}/etc/login.defs"
}
parse_file() {
while read -r conf; do
lineno=0
while read -r line; do
parse_string "${line}" "$((lineno += 1))"
done < "${conf}"
[ -n "${line}" ] && parse_string "${line}"
done
}
parse_string() {
[ -n "${1%%#*}" ] || return
full_line=$1
#eval "set -- $1" # do not use eval, see CVE-2021-40084
set -- $1
suffix="${1#?}"
type="${1%%${suffix}}" name="$2" id="$3" gecos="$4" home="$5"
# and now set the GECOS field without eval
if [ "${type}" = u ]; then
if [ ! -z "$4" ] && [ "$4" != '-' ]; then
# strip everything before the first "
gecosplus=${full_line#*\"}
# now strip everything after the last "
gecos=${gecosplus%\"*}
# check if there are other valid fields after GECOS
gecostest=$(echo $gecosplus | grep -o '".*' -)
if [ "$gecostest" = '"' ]; then
home=
else
set -- $gecostest
home=$2
fi
fi
fi
case "${type}" in
u)
uid=${id%%:*}
gid=${id##*:}
case "${uid}" in 65535|4294967295) warninvalid; return; esac
case "${gid}" in 65535|4294967295) warninvalid; return; esac
if [ "${uid}" != '-' ] && [ "${gid}" = '-' ]; then warninvalid; return; fi
[ "${home:--}" = '-' ] && home='/'
if [ "${uid}" = "${id}" ]; then
# No specific gid, create group for this user
add_group "${name}" "${id}"
fi
case "${suffix}" in
'!') locked=1;;
'') ;;
*) warninvalid; return;;
esac
add_user "${name}" "${uid}" "${gid}" "${gecos}" "${home}" ${locked:+locked}
;;
g)
case "${id}" in 65535|4294967295) warninvalid; return; esac
add_group "${name}" "${id}"
;;
m)
add_group "${id}" '-'
if id "${name}" >/dev/null 2>&1; then
usermod --prefix "$root" -a -G "${id}" "${name}"
else
useradd --prefix "$root" -r -g "${id}" -s '/sbin/nologin' "${name}"
passwd --prefix "$root" -l "${name}" >/dev/null 2>&1
fi
;;
r)
update_login_defs "${name}" "${id}"
;;
*) warninvalid; return ;;
esac
}
usage() {
printf '%s\n' \
"${0##*/}" '' \
"${0##*/} creates system users and groups, based on the file" \
'format and location specified in sysusers.d(5).' '' \
"Usage: ${0##*/} [OPTIONS...] [CONFIGFILE...]" '' \
'Options:' \
' --root=root All paths will be prefixed with the' \
' given alternate root path, including' \
' config search paths.' \
" --replace=PATH Don't run check in the package" \
' --inline Treat each positional argument as a' \
' separate configuration line instead of a' \
' file name.' \
' -h, --help Print a short help text and exit.' \
' --version Print a short version string and exit.'
exit "$1"
}
error=0 inline=0 replace='' root='' seen=''
# opensysusers is an implementation of sysusers.d spec without
# systemd command, it doesn't accept options or arguments
[ "${0##*/}" = opensysusers ] && set --
while [ "$#" -ne 0 ]; do
case "$1" in
--root=*) root="${1#--root=}" ;;
--root) root="$2"; shift ;;
--replace=*) replace="${1#--replace=}" ;;
--replace) replace="$2"; shift ;;
--inline) inline=1 ;;
--version) printf '%s\n' "${sysusersver}"; exit 0 ;;
-h|--help) usage 0 ;;
-[!-]|--?*) usage 1 ;;
--) shift; break ;;
*) break ;;
esac
shift
done
if [ "${inline}" -eq 0 ]; then
for file do
[ "${file}" = '--' ] && continue
for dir in etc run usr/lib; do
if [ -f "${root}/${dir}/sysusers.d/${file}" ]; then
sed -i -e '$a\' "${root}/${dir}/sysusers.d/${file}"
printf '%s/%s/sysusers.d/%s\n' "${root}" "${dir}" "${file}" |
parse_file
break
fi
done
done
else
for string in "$@"; do
parse_string "${string}"
done
fi
if [ "$#" -eq 0 ] || [ -n "${replace}" ]; then
set -- "${root}/etc/sysusers.d/"*.conf "${root}/run/sysusers.d/"*.conf \
"${root}/usr/lib/sysusers.d/"*.conf
for f do printf '%s %s\n' "${f##*/}" "${f%/*}"; done | sort -k1,1 |
while read -r b d; do
[ "${seen}" = "${seen#* ${b} }" ] && [ -f "${d}/${b}" ] &&
{ seen="${seen:- }${b} "; printf '%s/%s\n' "${d}" "${b}"; }
done | parse_file
fi
exit "${error}"
|