run0: add explicit support for opening sessions in specific areas

This commit is contained in:
Lennart Poettering
2025-01-24 10:24:09 +01:00
parent c747c04146
commit f44e7a8c11

View File

@@ -91,6 +91,7 @@ static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
static int arg_lightweight = -1;
static char *arg_area = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@@ -103,6 +104,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
STATIC_DESTRUCTOR_REGISTER(arg_area, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@@ -206,6 +208,7 @@ static int help_sudo_mode(void) {
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
" --lightweight=BOOLEAN Control whether to register a session with service manager\n"
" or without\n"
" -a --area=AREA Home area to log into\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -824,6 +827,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{ "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT },
{ "area", required_argument, NULL, 'a' },
{},
};
@@ -835,7 +839,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
/* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
optind = 0;
while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "+hVu:g:D:a:", options, NULL)) >= 0)
switch (c) {
@@ -942,6 +946,17 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
return r;
break;
case 'a':
/* We allow an empty --area= specification to allow logging into the primary home directory */
if (!isempty(optarg) && !filename_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid area name, refusing: %s", optarg);
r = free_and_strdup_warn(&arg_area, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@@ -949,6 +964,14 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
assert_not_reached();
}
if (!arg_exec_user && arg_area) {
/* If the user specifies --area= but not --user= then consider this an area switch request,
* and default to logging into our own account */
arg_exec_user = getusername_malloc();
if (!arg_exec_user)
return log_oom();
}
if (!arg_working_directory) {
if (arg_exec_user) {
/* When switching to a specific user, also switch to its home directory. */
@@ -1075,26 +1098,39 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
/* When using run0 to acquire privileges temporarily, let's not pull in session manager by
* default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
* escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
* manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
* this for root though, under the assumption that if a regular user temporarily transitions into
* another regular user it's a better default that the full user environment is uniformly
* available. */
if (arg_lightweight < 0 && !strv_env_get(arg_environment, "XDG_SESSION_CLASS") && privileged_execution())
arg_lightweight = true;
if (!strv_env_get(arg_environment, "XDG_SESSION_CLASS")) {
if (arg_lightweight >= 0) {
const char *class =
arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
(arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
/* If logging into an area, imply lightweight mode */
if (arg_lightweight < 0 && !isempty(arg_area))
arg_lightweight = true;
log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
/* When using run0 to acquire privileges temporarily, let's not pull in session manager by
* default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
* escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
* manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
* this for root though, under the assumption that if a regular user temporarily transitions into
* another regular user it's a better default that the full user environment is uniformly
* available. */
if (arg_lightweight < 0 && privileged_execution())
arg_lightweight = true;
r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
if (arg_lightweight >= 0) {
const char *class =
arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
(arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
if (r < 0)
return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
}
}
if (arg_area) {
r = strv_env_assign(&arg_environment, "XDG_AREA", arg_area);
if (r < 0)
return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
return log_error_errno(r, "Failed to set $XDG_AREA environment variable: %m");
}
return 1;