sysusers: validate shell credentials

Validate passwd.shell.<user> credentials with the same login-shell
rules used for sysusers.d shell fields before writing passwd entries.

Reproducer: CREDENTIALS_DIRECTORY=... systemd-sysusers --root=...
with --inline 'u creduser 999 "Cred User" / -'

Before: relative-shell was written as the login shell and the command
exited successfully.

Follow-up: 99e9f896fb
This commit is contained in:
dongshengyuan
2026-07-18 15:48:34 +08:00
parent 8729fa0e4b
commit 05b49871a2
2 changed files with 48 additions and 9 deletions
+39 -9
View File
@@ -468,6 +468,35 @@ static const char* pick_shell(const Item *i) {
return NOLOGIN;
}
static int read_shell_credential(const Item *i, char **ret) {
_cleanup_free_ char *cn = NULL, *shell = NULL;
int r;
assert(i);
cn = strjoin("passwd.shell.", i->name);
if (!cn)
return -ENOMEM;
r = read_credential(cn, (void**) &shell, /* ret_size= */ NULL);
if (r < 0) {
log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
if (ret)
*ret = NULL;
return 0;
}
path_simplify(shell);
if (!valid_shell(shell))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Shell credential '%s' specifies invalid login shell '%s'.",
cn, shell);
if (ret)
*ret = TAKE_PTR(shell);
return 0;
}
static int write_temporary_passwd(
Context *c,
const char *passwd_path,
@@ -488,6 +517,12 @@ static int write_temporary_passwd(
goto done;
if (arg_dry_run) {
ORDERED_HASHMAP_FOREACH(i, c->todo_uids) {
r = read_shell_credential(i, /* ret= */ NULL);
if (r < 0)
return r;
}
log_info("Would write /etc/passwd%s", glyph(GLYPH_ELLIPSIS));
goto done;
}
@@ -540,7 +575,7 @@ static int write_temporary_passwd(
}
ORDERED_HASHMAP_FOREACH(i, c->todo_uids) {
_cleanup_free_ char *creds_shell = NULL, *cn = NULL;
_cleanup_free_ char *creds_shell = NULL;
struct passwd n = {
.pw_name = i->name,
@@ -559,15 +594,10 @@ static int write_temporary_passwd(
.pw_shell = (char*) pick_shell(i),
};
/* Try to pick up the shell for this account via the credentials logic */
cn = strjoin("passwd.shell.", i->name);
if (!cn)
return -ENOMEM;
r = read_credential(cn, (void**) &creds_shell, NULL);
r = read_shell_credential(i, &creds_shell);
if (r < 0)
log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
else
return r;
if (creds_shell)
n.pw_shell = creds_shell;
r = putpwent_sane(&n, passwd);
+9
View File
@@ -30,6 +30,15 @@ at_exit() {
trap at_exit EXIT
root="$(mktemp -d)"
cred="$(mktemp -d)"
mkdir -p "$root/etc"
printf 'relative-shell' >"$cred/passwd.shell.creduser"
(! env CREDENTIALS_DIRECTORY="$cred" systemd-sysusers --dry-run --root="$root" --inline 'u creduser 999 "Cred User" / -')
(! env CREDENTIALS_DIRECTORY="$cred" systemd-sysusers --root="$root" --inline 'u creduser 999 "Cred User" / -')
(! grep -F creduser "$root/etc/passwd" >/dev/null 2>&1)
rm -rf "$root" "$cred"
# Ensure that a non-responsive NSS socket doesn't make sysusers fail
mount -t tmpfs tmpfs /run/systemd/userdb/
touch /run/systemd/userdb/io.systemd.DynamicUser