]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sysusers: validate shell credentials
authordongshengyuan <545258830@qq.com>
Thu, 16 Jul 2026 06:21:02 +0000 (14:21 +0800)
committerdongshengyuan <545258830@qq.com>
Sat, 18 Jul 2026 07:48:34 +0000 (15:48 +0800)
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: 99e9f896fb491d13c6d02f6f5bbfceabae833f05

src/sysusers/sysusers.c
test/units/TEST-74-AUX-UTILS.sysusers.sh

index 0970b76de23a03ff0b23429e7604bd00c5f4c69f..8eb1ae13c597a47f089f66eba6197308fe6634d0 100644 (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);
index 6e3fcac8c56e201b59b2427043f3d9e1230b2b01..3322d337d8f9c031a7ba02588661df5ac26cca3d 100755 (executable)
@@ -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