user-util: return malloc'ed strings in get_user_creds

get_user_creds would use getpwnam() and then returns strings pointing
into the static buffer. This seems very iffy. Let's duplicate the
strings properly
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-05-21 18:02:05 +02:00
parent 3ecd6b69cf
commit c83ff0e58d
21 changed files with 176 additions and 203 deletions
+28 -53
View File
@@ -877,31 +877,6 @@ restore_stdio:
return r;
}
static int get_fixed_user(
const char *user_or_uid,
bool prefer_nss,
const char **ret_username,
uid_t *ret_uid,
gid_t *ret_gid,
const char **ret_home,
const char **ret_shell) {
int r;
assert(user_or_uid);
assert(ret_username);
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell,
USER_CREDS_CLEAN|(prefer_nss ? USER_CREDS_PREFER_NSS : 0));
if (r < 0)
return r;
/* user_or_uid is normalized by get_user_creds to username */
*ret_username = user_or_uid;
return 0;
}
static int get_supplementary_groups(
const ExecContext *c,
const char *user,
@@ -2015,6 +1990,7 @@ static int build_environment(
char ***ret) {
_cleanup_strv_free_ char **e = NULL;
_cleanup_free_ char *_username = NULL, *_home = NULL, *_shell = NULL;
size_t n = 0;
pid_t exec_pid;
int r;
@@ -2079,12 +2055,16 @@ static int build_environment(
if (!username && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
assert(!c->user);
r = get_fixed_user("root", /* prefer_nss= */ false, &username, NULL, NULL, &home, &shell);
r = get_user_creds("root", USER_CREDS_CLEAN, &_username, NULL, NULL, &_home, &_shell);
if (r < 0) {
log_debug_errno(r, "Failed to determine credentials for user root: %s",
STRERROR_USER(r));
return ERRNO_IS_NEG_BAD_ACCOUNT(r) ? -EINVAL : r; /* Suppress confusing errno */
}
username = _username;
home = _home;
shell = _shell;
}
bool set_user_login_env = exec_context_get_set_login_environment(c);
@@ -4347,16 +4327,13 @@ static int send_user_lookup(
return 0;
}
static int acquire_home(const ExecContext *c, const char **home, char **ret_buf) {
int r;
static int acquire_home(const ExecContext *c, char **home) {
assert(c);
assert(home);
assert(ret_buf);
/* If WorkingDirectory=~ is set, try to acquire a usable home directory. */
if (*home) /* Already acquired from get_fixed_user()? */
if (*home) /* Already acquired from get_user_creds()? */
return 0;
if (!c->working_directory_home)
@@ -4365,12 +4342,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf)
if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
return -EADDRNOTAVAIL;
r = get_home_dir(ret_buf);
if (r < 0)
return r;
*home = *ret_buf;
return 1;
return get_home_dir(home);
}
static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p, char ***ret) {
@@ -5162,9 +5134,8 @@ int exec_invoke(
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL;
int r;
const char *username = NULL;
_cleanup_free_ char *home_buffer = NULL, *own_user = NULL;
_cleanup_free_ char *pwent_home = NULL, *shell = NULL, *_own_user = NULL, *_username = NULL;
_cleanup_(free_pressure_paths) char *pressure_path[_PRESSURE_RESOURCE_MAX] = {};
const char *pwent_home = NULL, *shell = NULL;
dev_t journal_stream_dev = 0;
ino_t journal_stream_ino = 0;
bool needs_sandboxing, /* Do we need to set up full sandboxing? (i.e. all namespacing, all MAC stuff, caps, yadda yadda */
@@ -5400,36 +5371,40 @@ int exec_invoke(
username = runtime->dynamic_creds->user->name;
} else {
const char *u;
if (context->user)
u = context->user;
username = context->user;
else if (context->pam_name || FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL)) {
/* If PAM is enabled but no user name is explicitly selected, then use our own one. */
own_user = getusername_malloc();
if (!own_user) {
username = _own_user = getusername_malloc();
if (!username) {
*exit_status = EXIT_USER;
return log_oom();
}
u = own_user;
} else
u = NULL;
}
if (u) {
if (username) {
/* We can't use nss unconditionally for root without risking deadlocks if some IPC services
* will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
* enabled *explicitly* (i.e. no exec_context_get_set_login_environment() here),
* or PAM shall be invoked, let's consult NSS even for root, so that the user
* gets accurate $SHELL in session(-like) contexts. */
r = get_fixed_user(u,
/* prefer_nss= */ context->set_login_environment > 0 || context->pam_name,
&username, &uid, &gid, &pwent_home, &shell);
bool prefer_nss = context->set_login_environment > 0 || context->pam_name;
r = get_user_creds(username,
USER_CREDS_CLEAN|(prefer_nss ? USER_CREDS_PREFER_NSS : 0),
&_username,
&uid,
&gid,
&pwent_home,
&shell);
if (r < 0) {
*exit_status = EXIT_USER;
log_error_errno(r, "Failed to determine credentials for user '%s': %s",
u, STRERROR_USER(r));
username, STRERROR_USER(r));
return ERRNO_IS_NEG_BAD_ACCOUNT(r) ? -EINVAL : r; /* Suppress confusing errno */
}
username = _username;
}
if (context->group) {
@@ -5460,7 +5435,7 @@ int exec_invoke(
params->user_lookup_fd = safe_close(params->user_lookup_fd);
}
r = acquire_home(context, &pwent_home, &home_buffer);
r = acquire_home(context, &pwent_home);
if (r < 0) {
*exit_status = EXIT_CHDIR;
return log_error_errno(r, "Failed to determine $HOME for the invoking user: %m");
+2 -4
View File
@@ -344,13 +344,11 @@ static int scope_enter_start_chown(Scope *s) {
gid_t gid = GID_INVALID;
if (!isempty(s->user)) {
const char *user = s->user;
r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
r = get_user_creds(s->user, /* flags= */ 0, NULL, &uid, &gid, NULL, NULL);
if (r < 0) {
log_unit_error_errno(UNIT(s), r,
"Failed to resolve user '%s': %s",
user, STRERROR_USER(r));
s->user, STRERROR_USER(r));
_exit(EXIT_USER);
}
}
+2 -4
View File
@@ -2045,13 +2045,11 @@ static int socket_chown(Socket *s, PidRef *ret_pid) {
/* Child */
if (!isempty(s->user)) {
const char *user = s->user;
r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
r = get_user_creds(s->user, /* flags= */ 0, NULL, &uid, &gid, NULL, NULL);
if (r < 0) {
log_unit_error_errno(UNIT(s), r,
"Failed to resolve user '%s': %s",
user, STRERROR_USER(r));
s->user, STRERROR_USER(r));
_exit(EXIT_USER);
}
}