]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
run0: add explicit support for opening sessions in specific areas
authorLennart Poettering <lennart@poettering.net>
Fri, 24 Jan 2025 09:24:09 +0000 (10:24 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 6 Feb 2025 08:23:49 +0000 (09:23 +0100)
src/run/run.c

index 68966ccbda29d0b06104b4127bce78d783c799d0..8d1606af07c8f30ac208ab6f2d59497aab89ca1c 100644 (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 logging into an area, imply lightweight mode */
+                if (arg_lightweight < 0 && !isempty(arg_area))
+                        arg_lightweight = true;
+
+                /* 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;
+
+                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 (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);
 
-                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");
+                }
+        }
 
-                r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
+        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;