Compare commits
1 Commits
4ad4750520
...
28c3935d86
Author | SHA1 | Date |
---|---|---|
Dan Streetman | 28c3935d86 |
|
@ -220,9 +220,9 @@ static int synthesize_user_creds(
|
||||||
if (ret_gid)
|
if (ret_gid)
|
||||||
*ret_gid = GID_NOBODY;
|
*ret_gid = GID_NOBODY;
|
||||||
if (ret_home)
|
if (ret_home)
|
||||||
*ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
|
*ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
|
||||||
if (ret_shell)
|
if (ret_shell)
|
||||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
|
*ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,6 @@ int get_user_creds(
|
||||||
|
|
||||||
assert(username);
|
assert(username);
|
||||||
assert(*username);
|
assert(*username);
|
||||||
assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
|
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
||||||
(!ret_home && !ret_shell)) {
|
(!ret_home && !ret_shell)) {
|
||||||
|
@ -316,14 +315,17 @@ int get_user_creds(
|
||||||
|
|
||||||
if (ret_home)
|
if (ret_home)
|
||||||
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
||||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
|
*ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
|
(empty_or_root(p->pw_dir) ||
|
||||||
? NULL : p->pw_dir;
|
!path_is_valid(p->pw_dir) ||
|
||||||
|
!path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
|
||||||
|
|
||||||
if (ret_shell)
|
if (ret_shell)
|
||||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
|
*ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
|
(isempty(p->pw_shell) ||
|
||||||
? NULL : p->pw_shell;
|
!path_is_valid(p->pw_shell) ||
|
||||||
|
!path_is_absolute(p->pw_shell) ||
|
||||||
|
is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
|
||||||
|
|
||||||
if (patch_username)
|
if (patch_username)
|
||||||
*username = p->pw_name;
|
*username = p->pw_name;
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "string-util.h"
|
|
||||||
|
|
||||||
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
||||||
#define HOME_UID_MIN ((uid_t) 60001)
|
#define HOME_UID_MIN ((uid_t) 60001)
|
||||||
#define HOME_UID_MAX ((uid_t) 60513)
|
#define HOME_UID_MAX ((uid_t) 60513)
|
||||||
|
@ -38,20 +36,10 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
|
||||||
char* getlogname_malloc(void);
|
char* getlogname_malloc(void);
|
||||||
char* getusername_malloc(void);
|
char* getusername_malloc(void);
|
||||||
|
|
||||||
const char* default_root_shell_at(int rfd);
|
|
||||||
const char* default_root_shell(const char *root);
|
|
||||||
|
|
||||||
bool is_nologin_shell(const char *shell);
|
|
||||||
|
|
||||||
static inline bool shell_is_placeholder(const char *shell) {
|
|
||||||
return isempty(shell) || is_nologin_shell(shell);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum UserCredsFlags {
|
typedef enum UserCredsFlags {
|
||||||
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
||||||
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
||||||
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
||||||
USER_CREDS_SUPPRESS_PLACEHOLDER = 1 << 3, /* suppress home and/or shell fields if value is placeholder (root/empty/nologin) */
|
|
||||||
} UserCredsFlags;
|
} UserCredsFlags;
|
||||||
|
|
||||||
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
||||||
|
@ -137,6 +125,10 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg);
|
||||||
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool is_nologin_shell(const char *shell);
|
||||||
|
const char* default_root_shell_at(int rfd);
|
||||||
|
const char* default_root_shell(const char *root);
|
||||||
|
|
||||||
int is_this_me(const char *username);
|
int is_this_me(const char *username);
|
||||||
|
|
||||||
const char* get_home_root(void);
|
const char* get_home_root(void);
|
||||||
|
|
|
@ -855,6 +855,9 @@ static int get_fixed_user(
|
||||||
assert(user_or_uid);
|
assert(user_or_uid);
|
||||||
assert(ret_username);
|
assert(ret_username);
|
||||||
|
|
||||||
|
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||||
|
* (i.e. are "/" or "/bin/nologin"). */
|
||||||
|
|
||||||
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1880,10 +1883,7 @@ static int build_environment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
if (home && set_user_login_env) {
|
||||||
* (i.e. are "/" or "/bin/nologin"). */
|
|
||||||
|
|
||||||
if (home && set_user_login_env && !empty_or_root(home)) {
|
|
||||||
x = strjoin("HOME=", home);
|
x = strjoin("HOME=", home);
|
||||||
if (!x)
|
if (!x)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1892,7 +1892,7 @@ static int build_environment(
|
||||||
our_env[n_env++] = x;
|
our_env[n_env++] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shell && set_user_login_env && !shell_is_placeholder(shell)) {
|
if (shell && set_user_login_env) {
|
||||||
x = strjoin("SHELL=", shell);
|
x = strjoin("SHELL=", shell);
|
||||||
if (!x)
|
if (!x)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -3471,16 +3471,20 @@ static int apply_working_directory(
|
||||||
const ExecContext *context,
|
const ExecContext *context,
|
||||||
const ExecParameters *params,
|
const ExecParameters *params,
|
||||||
ExecRuntime *runtime,
|
ExecRuntime *runtime,
|
||||||
const char *home) {
|
const char *home,
|
||||||
|
int *exit_status) {
|
||||||
|
|
||||||
const char *wd;
|
const char *wd;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
|
assert(exit_status);
|
||||||
|
|
||||||
if (context->working_directory_home) {
|
if (context->working_directory_home) {
|
||||||
if (!home)
|
if (!home) {
|
||||||
|
*exit_status = EXIT_CHDIR;
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
wd = home;
|
wd = home;
|
||||||
} else
|
} else
|
||||||
|
@ -3499,7 +3503,13 @@ static int apply_working_directory(
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
r = RET_NERRNO(fchdir(dfd));
|
r = RET_NERRNO(fchdir(dfd));
|
||||||
}
|
}
|
||||||
return context->working_directory_missing_ok ? 0 : r;
|
|
||||||
|
if (r < 0 && !context->working_directory_missing_ok) {
|
||||||
|
*exit_status = EXIT_CHDIR;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apply_root_directory(
|
static int apply_root_directory(
|
||||||
|
@ -3775,7 +3785,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf)
|
||||||
if (!c->working_directory_home)
|
if (!c->working_directory_home)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
|
if (c->dynamic_user)
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
|
|
||||||
r = get_home_dir(ret_buf);
|
r = get_home_dir(ret_buf);
|
||||||
|
@ -4533,7 +4543,7 @@ int exec_invoke(
|
||||||
r = acquire_home(context, &home, &home_buffer);
|
r = acquire_home(context, &home, &home_buffer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_CHDIR;
|
*exit_status = EXIT_CHDIR;
|
||||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking user: %m");
|
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for user: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
||||||
|
@ -5372,11 +5382,9 @@ int exec_invoke(
|
||||||
* running this service might have the correct privilege to change to the working directory. Also, it
|
* running this service might have the correct privilege to change to the working directory. Also, it
|
||||||
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
||||||
* the cwd cannot be used to pin directories outside of the sandbox. */
|
* the cwd cannot be used to pin directories outside of the sandbox. */
|
||||||
r = apply_working_directory(context, params, runtime, home);
|
r = apply_working_directory(context, params, runtime, home, exit_status);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
*exit_status = EXIT_CHDIR;
|
|
||||||
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
||||||
}
|
|
||||||
|
|
||||||
if (needs_sandboxing) {
|
if (needs_sandboxing) {
|
||||||
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
||||||
|
|
|
@ -2297,8 +2297,7 @@ static int start_transient_scope(sd_bus *bus) {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
|
||||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
|
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS);
|
||||||
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
|
|
||||||
set -eux
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# shellcheck source=test/units/util.sh
|
|
||||||
. "$(dirname "$0")"/util.sh
|
|
||||||
|
|
||||||
(! systemd-run --wait -p DynamicUser=yes \
|
|
||||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
|
||||||
-p WorkingDirectory='~' true)
|
|
||||||
|
|
||||||
assert_eq "$(systemd-run --pipe --uid=root -p WorkingDirectory='~' pwd)" "/root"
|
|
||||||
assert_eq "$(systemd-run --pipe --uid=nobody -p WorkingDirectory='~' pwd)" "/"
|
|
||||||
assert_eq "$(systemd-run --pipe --uid=testuser -p WorkingDirectory='~' pwd)" "/home/testuser"
|
|
||||||
|
|
||||||
(! systemd-run --wait -p DynamicUser=yes -p User=testuser \
|
|
||||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
|
||||||
-p WorkingDirectory='~' true)
|
|
|
@ -16,7 +16,6 @@ ConditionDirectoryNotEmpty=|/run/confexts
|
||||||
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
||||||
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
||||||
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
||||||
ConditionDirectoryNotEmpty=|/.extra/confext
|
|
||||||
|
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
After=local-fs.target
|
After=local-fs.target
|
||||||
|
|
Loading…
Reference in New Issue