From 26fd286210964a76c5e1a52a416626f7dde53936 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Apr 2026 20:21:25 +0100 Subject: [PATCH 1/5] core: add missing SELinux access checks when listing units Add mac_selinux_unit_access_check_varlink() to the unit enumeration loop in vl_method_list_units(), silently skipping units the caller is not permitted to see, matching the D-Bus ListUnits behavior. Add mac_selinux_access_check_varlink() to vl_method_describe_manager(). Follow-up for 472abf7bec89caeb1cc413c1de17984ab8ccb5d6 Follow-up for 736349958efe34089131ca88950e2e5bb391d36a --- src/core/varlink-manager.c | 4 ++++ src/core/varlink-unit.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index 997bdc08d0..6ae837376a 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -207,6 +207,10 @@ int vl_method_describe_manager(sd_varlink *link, sd_json_variant *parameters, sd if (r != 0) return r; + r = mac_selinux_access_check_varlink(link, "status"); + if (r < 0) + return r; + r = sd_json_buildo( &v, SD_JSON_BUILD_PAIR_CALLBACK("context", manager_context_build_json, manager), diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index 2404c553d7..e8b86845a2 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -523,6 +523,10 @@ int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varli if (k != unit->id) continue; + r = mac_selinux_unit_access_check_varlink(unit, link, "status"); + if (r < 0) + continue; /* silently skip units the caller is not allowed to see */ + r = list_unit_one(link, unit); if (r < 0) return r; From 04f32dddd7221de01c4da70128bd5fb21bc53427 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Apr 2026 22:11:27 +0100 Subject: [PATCH 2/5] core: check selinux access on each unit when listing Units might have different access rules, so check the access on each unit when querying the full list. --- src/core/dbus-manager.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 78cab48f85..5a7f70d78b 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1265,10 +1265,6 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e /* Anyone can call this method */ - r = mac_selinux_access_check(message, "status", reterr_error); - if (r < 0) - return r; - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -1281,6 +1277,10 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e if (k != u->id) continue; + r = mac_selinux_unit_access_check(u, message, "status", /* reterr_error= */ NULL); + if (r < 0) + continue; /* silently skip units the caller is not allowed to see */ + if (!unit_passes_filter(u, states, patterns)) continue; From 4bf9db731445ba72a9e5097561e1883dfe1183d8 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Apr 2026 20:26:22 +0100 Subject: [PATCH 3/5] logind: reject wall messages containing control characters method_set_wall_message() and the property setter only checked the message length but not its content. Since wall messages are broadcast to all TTYs, control characters in the message could interfere with terminal state. Reject messages containing control characters other than newline and tab. Follow-up for 9ef15026c0e7e6600372056c43442c99ec53746e Follow-up for e2fa5721c3ee5ea400b99a6463e8c1c257e20415 --- src/login/logind-dbus.c | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index bef9f51aef..9668bf0609 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -59,6 +59,7 @@ #include "signal-util.h" #include "sleep-config.h" #include "stdio-util.h" +#include "string-util.h" #include "strv.h" #include "terminal-util.h" #include "tmpfile-util.h" @@ -3582,6 +3583,46 @@ static int property_get_boot_loader_entries( return sd_bus_message_close_container(reply); } +static int wall_message_validate(const char *wall_message, sd_bus_error *error) { + if (strlen(wall_message) > WALL_MESSAGE_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Wall message too long, maximum permitted length is %u characters.", + WALL_MESSAGE_MAX); + + if (string_has_cc(wall_message, /* ok= */ "\n\t")) + return sd_bus_error_set(error, + SD_BUS_ERROR_INVALID_ARGS, + "Wall message contains control characters, refusing."); + + return 0; +} + +static int property_set_wall_message( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + char **p = ASSERT_PTR(userdata); + const char *s; + int r; + + assert(value); + + r = sd_bus_message_read(value, "s", &s); + if (r < 0) + return r; + + r = wall_message_validate(s, error); + if (r < 0) + return r; + + return free_and_strdup_warn(p, empty_to_null(s)); +} + static int method_set_wall_message( sd_bus_message *message, void *userdata, @@ -3598,10 +3639,9 @@ static int method_set_wall_message( if (r < 0) return r; - if (strlen(wall_message) > WALL_MESSAGE_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, - "Wall message too long, maximum permitted length is %u characters.", - WALL_MESSAGE_MAX); + r = wall_message_validate(wall_message, error); + if (r < 0) + return r; /* Short-circuit the operation if the desired state is already in place, to * avoid an unnecessary polkit permission check. */ @@ -3764,7 +3804,7 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, wall_messages), 0), - SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), + SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, property_set_wall_message, offsetof(Manager, wall_message), 0), SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), From a9e9288288567beae57337ae903dd3b6c774001c Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Apr 2026 20:38:27 +0100 Subject: [PATCH 4/5] machined: pass user as positional argument in machine_default_shell_args() Instead of interpolating the user name directly into the sh -c script body via asprintf %s, pass it as a positional parameter ($1) in a separate argv entry. This avoids the user string being parsed as part of the shell script syntax. Also validate the user name in bus_machine_method_open_shell() with valid_user_group_name(), matching the validation already done on the Varlink path via json_dispatch_const_user_group_name(). Follow-up for 49af9e1368571f4e423cde0fd45ee284451434d1 --- src/machine/machine-dbus.c | 4 ++++ src/machine/machine.c | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 2dab827d41..c096a012d8 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -26,6 +26,7 @@ #include "signal-util.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass); static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", Machine, machine_get_state, machine_state_to_string); @@ -366,6 +367,9 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu return r; user = isempty(user) ? "root" : user; + if (!valid_user_group_name(user, VALID_USER_RELAX)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name '%s'", user); + /* Ensure only root can shell into the root namespace. This is to avoid unprivileged users registering * a process they own in the root user namespace, and then shelling in as root or another user. Note that * the shell operation is privileged and requires 'auth_admin', so we do not need to check the caller's uid, diff --git a/src/machine/machine.c b/src/machine/machine.c index da26bdf7a0..535128692e 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -1103,11 +1103,10 @@ int machine_start_shell( char** machine_default_shell_args(const char *user) { _cleanup_strv_free_ char **args = NULL; - int r; assert(user); - args = new0(char*, 3 + 1); + args = new0(char*, 5 + 1); if (!args) return NULL; @@ -1119,14 +1118,19 @@ char** machine_default_shell_args(const char *user) { if (!args[1]) return NULL; - r = asprintf(&args[2], - "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\ - "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */ - user); - if (r < 0) { - args[2] = NULL; + args[2] = strdup( + "shell=$(getent passwd \"$1\" 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n" + "exec \"${shell:-/bin/sh}\" -l"); /* -l means --login */ + if (!args[2]) + return NULL; + + args[3] = strdup("sh"); /* $0 placeholder for sh -c */ + if (!args[3]) + return NULL; + + args[4] = strdup(user); /* becomes $1 in the script */ + if (!args[4]) return NULL; - } return TAKE_PTR(args); } From f125fc6a22167f3d52c97763e555b2d7d654788e Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Apr 2026 21:02:10 +0100 Subject: [PATCH 5/5] journal-upload: also disable VERIFYHOST when --trust=all is used When --trust=all disables CURLOPT_SSL_VERIFYPEER, the residual CURLOPT_SSL_VERIFYHOST check is ineffective since an attacker can present a self-signed certificate with the expected hostname. Disable both for consistency and log that server certificate verification is disabled. Follow-up for 8847551bcbfa8265bae04f567bb1aadc7b480325 --- src/journal-remote/journal-upload.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index c4eab80a1f..e6cb5dabc2 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -307,10 +307,13 @@ int start_upload(Uploader *u, LOG_ERR, return -EXFULL); } - if (STRPTR_IN_SET(arg_trust, "-", "all")) + if (STRPTR_IN_SET(arg_trust, "-", "all")) { + log_info("Server certificate verification disabled."); easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L, LOG_ERR, return -EUCLEAN); - else if (arg_trust || startswith(u->url, "https://")) + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L, + LOG_ERR, return -EUCLEAN); + } else if (arg_trust || startswith(u->url, "https://")) easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE, LOG_ERR, return -EXFULL);