Compare commits

...

10 Commits

Author SHA1 Message Date
Michal Sekletar d0101552e4
Merge 50a367eb1e into 52b0351a15 2024-11-20 17:00:21 +07:00
Yu Watanabe 52b0351a15
core/exec-invoke: suppress placeholder home only in build_environment() (#35219)
Alternative to https://github.com/systemd/systemd/pull/34789
Closes #34789
2024-11-20 17:34:25 +09:00
Luca Boccassi fe077a1a58 units: add initrd directory to list of conditions for systemd-confext
systemd-sysext has the same check, but it was forgotten for confexts.
Needed to activate confexts from the ESP in the initrd.
2024-11-20 09:12:24 +01:00
Xuanjun Wen a526b9ddfc hwdb: add new Cube Mix Plus (i18D) rotation info
Added rotation information for the new version of Cube Mix Plus (i18D).
2024-11-20 05:23:34 +09:00
Mike Yuan 804dd670d1
sd-varlink: mark sd_varlink_server_{ref,unref} as _public_ (#35241)
Co-authored-by: Thorsten Kukuk <kukuk@suse.com>
2024-11-20 05:21:15 +09:00
Mike Yuan b718b86e1b
core/exec-invoke: suppress placeholder home only in build_environment()
Currently, get_fixed_user() employs USER_CREDS_SUPPRESS_PLACEHOLDER,
meaning home path is set to NULL if it's empty or root. However,
the path is also used for applying WorkingDirectory=~, and we'd
spuriously use the invoking user's home as fallback even if
User= is changed in that case.

Let's instead delegate such suppression to build_environment(),
so that home is proper initialized for usage at other steps.
shell doesn't actually suffer from such problem, but it's changed
too for consistency.

Alternative to #34789
2024-11-19 00:38:18 +01:00
Mike Yuan d911778877
core/exec-invoke: minor cleanup for apply_working_directory() error handling
Assign exit_status at the same site where error log is emitted,
for readability.
2024-11-19 00:38:18 +01:00
Mike Yuan eea9d3eb10
basic/user-util: split out placeholder suppression from USER_CREDS_CLEAN into its own flag
No functional change, preparation for later commits.
2024-11-19 00:38:18 +01:00
Mike Yuan 579ce77ead
basic/user-util: introduce shell_is_placeholder() helper 2024-11-19 00:38:18 +01:00
Michal Sekletar 50a367eb1e man: adjust description of PrivateUsers= so it is in line with reality
When the option is not available unit will not even start so there is
no security risk.

Fixes #34983
2024-11-15 23:11:09 +01:00
10 changed files with 66 additions and 47 deletions

View File

@ -376,11 +376,12 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svncube:pni1-TF:*
sensor:modalias:acpi:SMO8500*:dmi:*:svncube:pni7:* sensor:modalias:acpi:SMO8500*:dmi:*:svncube:pni7:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
# Cube i7 Stylus, i7 Stylus I8L Model, i7 Book (i16) and Mix Plus (i18B) # Cube i7 Stylus, i7 Stylus I8L Model, i7 Book (i16) and Mix Plus (i18B/i18D)
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni7Stylus:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni7Stylus:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni8-L:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni8-L:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni16:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni16:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni18B:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni18B:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnALLDOCUBE:pni18D:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
# Cube iWork 10 Flagship # Cube iWork 10 Flagship

View File

@ -2045,10 +2045,6 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
databases in the root directory and on the host is reduced, as the only users and groups who need to be matched databases in the root directory and on the host is reduced, as the only users and groups who need to be matched
are <literal>root</literal>, <literal>nobody</literal> and the unit's own user and group.</para> are <literal>root</literal>, <literal>nobody</literal> and the unit's own user and group.</para>
<para>Note that the implementation of this setting might be impossible (for example if user namespaces are not
available), and the unit should be written in a way that does not solely rely on this setting for
security.</para>
<xi:include href="version-info.xml" xpointer="v232"/></listitem> <xi:include href="version-info.xml" xpointer="v232"/></listitem>
</varlistentry> </varlistentry>

View File

@ -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_CLEAN) ? NULL : "/"; *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
if (ret_shell) if (ret_shell)
*ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN; *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
return 0; return 0;
} }
@ -244,6 +244,7 @@ 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)) {
@ -315,17 +316,14 @@ 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_CLEAN) && *ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
(empty_or_root(p->pw_dir) || (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
!path_is_valid(p->pw_dir) || ? NULL : 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_CLEAN) && *ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
(isempty(p->pw_shell) || (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
!path_is_valid(p->pw_shell) || ? NULL : 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;

View File

@ -12,6 +12,8 @@
#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)
@ -36,10 +38,20 @@ 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);
@ -125,10 +137,6 @@ 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);

View File

@ -855,9 +855,6 @@ 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;
@ -1883,7 +1880,10 @@ static int build_environment(
} }
} }
if (home && set_user_login_env) { /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
* (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) { if (shell && set_user_login_env && !shell_is_placeholder(shell)) {
x = strjoin("SHELL=", shell); x = strjoin("SHELL=", shell);
if (!x) if (!x)
return -ENOMEM; return -ENOMEM;
@ -3471,20 +3471,16 @@ 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
@ -3503,13 +3499,7 @@ 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(
@ -3785,7 +3775,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) if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
r = get_home_dir(ret_buf); r = get_home_dir(ret_buf);
@ -4543,7 +4533,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 user: %m"); return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking 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 */
@ -5382,9 +5372,11 @@ 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, exit_status); r = apply_working_directory(context, params, runtime, home);
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

View File

@ -1033,12 +1033,14 @@ global:
sd_varlink_server_listen_fd; sd_varlink_server_listen_fd;
sd_varlink_server_loop_auto; sd_varlink_server_loop_auto;
sd_varlink_server_new; sd_varlink_server_new;
sd_varlink_server_ref;
sd_varlink_server_set_connections_max; sd_varlink_server_set_connections_max;
sd_varlink_server_set_connections_per_uid_max; sd_varlink_server_set_connections_per_uid_max;
sd_varlink_server_set_description; sd_varlink_server_set_description;
sd_varlink_server_set_exit_on_idle; sd_varlink_server_set_exit_on_idle;
sd_varlink_server_set_userdata; sd_varlink_server_set_userdata;
sd_varlink_server_shutdown; sd_varlink_server_shutdown;
sd_varlink_server_unref;
sd_varlink_set_allow_fd_passing_input; sd_varlink_set_allow_fd_passing_input;
sd_varlink_set_allow_fd_passing_output; sd_varlink_set_allow_fd_passing_output;
sd_varlink_set_description; sd_varlink_set_description;

View File

@ -3265,7 +3265,7 @@ static sd_varlink_server* varlink_server_destroy(sd_varlink_server *s) {
return mfree(s); return mfree(s);
} }
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_varlink_server, sd_varlink_server, varlink_server_destroy); DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_varlink_server, sd_varlink_server, varlink_server_destroy);
static int validate_connection(sd_varlink_server *server, const struct ucred *ucred) { static int validate_connection(sd_varlink_server *server, const struct ucred *ucred) {
int allowed = -1; int allowed = -1;

View File

@ -2297,7 +2297,8 @@ 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, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS); r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
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);

View File

@ -0,0 +1,20 @@
#!/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)

View File

@ -16,6 +16,7 @@ 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