]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: introduce "user-light" session class
authorLennart Poettering <lennart@poettering.net>
Fri, 10 Jan 2025 08:32:18 +0000 (09:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 15 Jan 2025 16:03:21 +0000 (17:03 +0100)
This new session class is to "user" what "background" is to
"background-light": it doesn't cause the per-user service manager to
start.

This new session class is now the default if no session class was
provided at session registration time and the following conditions hold:

1. The session is not graphical
2. The user is not a regular user (but not root)

Or in other words root and system users won't get a service manager
started automatically if they go through a PAM session as part of things
like cron or ftp. They will however still get one if they log in
graphically.

This changes behaviour a bit, but hopefully in OK was.

This also makes "background-light" for system users incl. root.

This addresses one of the ideas discussed in #34988.

NEWS
TODO
man/pam_systemd.xml
man/sd_session_is_active.xml
src/login/logind-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/pam_systemd.c
src/shared/varlink-io.systemd.Login.c
test/units/TEST-35-LOGIN.sh

diff --git a/NEWS b/NEWS
index dde530e3964a7346c30cde51128f73b887af0dae..b6b1c8090722df2275f9a68768453a1b43815cd6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,23 @@ CHANGES WITH 258 in spe:
           enabled by default. This brings cmdline expansion of transient
           scopes on par with services.
 
+        * systemd-logind PAM sessions that previously were automatically
+          determined to be of class "background", and which are owned by root
+          or system accounts, will now automatically be set to class
+          "background-light" instead. PAM sessions that previously were
+          automatically determined to be of class "user", and which are owned
+          by non-root system users, will now automatically be set to class
+          "user-light" instead. This effectively means that cron jobs or FTP
+          sessions (i.e. all PAM sessions that have no TTY assigned and neither
+          are graphical) for system users no longer pull in a service manager
+          by default. This behaviour can be changed by explicitly setting the
+          session class (for example via the class= parameter to
+          pam_systemd.so, or by setting the XDG_SESSION_CLASS environment
+          variable as input for the service's PAM stack). This change does not
+          affect graphical sessions, nor does it affect regular users. This is
+          an incompatible change of sorts, since per-user services will
+          typically not be available for such PAM sessions of system users.
+
         Announcements of Future Feature Removals:
 
         * The D-Bus method org.freedesktop.systemd1.StartAuxiliaryScope() is
diff --git a/TODO b/TODO
index 48ab8eac84c5a71afb8e75c7c9868dff81ffcaa1..81d8151cae30dd169155a0c97edf87afd2772503 100644 (file)
--- a/TODO
+++ b/TODO
@@ -134,12 +134,6 @@ Features:
   really be recognizable via a message id and come with an explanatory catalog
   message
 
-* logind: introduce "user-light" session class, that is to "user" what
-  "background-light" is to "background". Then, in logind, if no user class is
-  specified, and we are not logging in graphically default to this session
-  class for non-regular users.  Effect: if you log into a system user for some
-  reason, yu won't get the service manager by default.
-
 * introduce new ANSI sequence for communicating log level and structured error
   metadata to terminals.
 
index 18c3636b6bf2aac3ac20ec2e712f5687dd6c3d36..1093df9f82d54a4d341f04f86379d477da74b9a9 100644 (file)
                 <entry><constant>user-early</constant></entry>
                 <entry>Similar to <literal>user</literal> but sessions of this class are not ordered after <citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, i.e. may be started before regular sessions are allowed to be established. This session class is the default for sessions of the root user that would otherwise qualify for the <constant>user</constant> class, see above. (Added in v256.)</entry>
               </row>
+              <row>
+                <entry><constant>user-light</constant></entry>
+                <entry>Similar to <constant>user</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v258.)</entry>
+              </row>
+              <row>
+                <entry><constant>user-early-light</constant></entry>
+                <entry>Similar to <constant>user-early</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v258.)</entry>
+              </row>
               <row>
                 <entry><constant>user-incomplete</constant></entry>
                 <entry>Similar to <literal>user</literal> but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> to allow users to log in via <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry> before their home directory is mounted, delaying the mount until the user provided the unlock password. Sessions of this class are upgraded to the regular <constant>user</constant> class once the home directory is activated.</entry>
               </row>
               <row>
                 <entry><constant>background-light</constant></entry>
-                <entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no services of the user running. (Added in v256.)</entry>
+                <entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v256.)</entry>
               </row>
               <row>
                 <entry><constant>manager</constant></entry>
index 1cbef64e003c14f2008e94e5bc8ea0555ff773bb..92cd669dc48c16c0c629bd7d97bc052359453c35 100644 (file)
 
     <para><function>sd_session_get_class()</function> may be used to determine the class of the session
     identified by the specified session identifier. The returned string is one of <literal>user</literal>,
-    <literal>user-early</literal>, <literal>user-incomplete</literal>, <literal>greeter</literal>,
-    <literal>lock-screen</literal>, <literal>background</literal>, <literal>background-light</literal>,
-    <literal>manager</literal> or <literal>manager-early</literal> and needs to be freed with the libc
-    <citerefentry
+    <literal>user-early</literal>, <literal>user-light</literal>, <literal>user-early-light</literal>,
+    <literal>user-incomplete</literal>, <literal>greeter</literal>, <literal>lock-screen</literal>,
+    <literal>background</literal>, <literal>background-light</literal>, <literal>manager</literal> or
+    <literal>manager-early</literal> and needs to be freed with the libc <citerefentry
     project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
     use.</para>
 
index ab39cc0644be12d07044bf4a1481277412b810aa..5d3e2e01b502361d9235a7a3e20fb0d1bb6d0c9c 100644 (file)
@@ -893,6 +893,7 @@ int manager_create_session(
                 const char *remote_host,
                 Session **ret_session) {
 
+        bool mangle_class = false;
         int r;
 
         assert(m);
@@ -920,6 +921,10 @@ int manager_create_session(
                         class = SESSION_BACKGROUND;
                 else
                         class = SESSION_USER;
+
+                /* If we determined the class automatically, then let's later potentially change it to early
+                 * or light flavours, once we learn the disposition of the user */
+                mangle_class = true;
         }
 
         /* Check if we are already in a logind session, and if so refuse. */
@@ -962,6 +967,25 @@ int manager_create_session(
         if (r < 0)
                 goto fail;
 
+        /* If we picked the session class on our own, and the user is not a regular one, and the session is
+         * not a graphical one then do not pull in session manager by default. For root make a special
+         * exception: for TTY logins leave the service manager on, but relax the /run/nologin
+         * restrictions. */
+        if (mangle_class &&
+            IN_SET(user_record_disposition(user->user_record), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC)) {
+
+                if (class == SESSION_USER) {
+                        if (user_record_is_root(user->user_record))
+                                class = SESSION_USER_EARLY;
+                        else if (SESSION_TYPE_IS_GRAPHICAL(type))
+                                class = SESSION_USER;
+                        else
+                                class = SESSION_USER_LIGHT;
+
+                } else if (class == SESSION_BACKGROUND)
+                        class = SESSION_BACKGROUND_LIGHT;
+        }
+
         r = manager_add_session(m, id, &session);
         if (r < 0)
                 goto fail;
index bc48609c26cdf32761184e8d0d6bff8b92caab26..686041320f3adedb9c551df8a82c6b0794891f6c 100644 (file)
@@ -1704,6 +1704,8 @@ static const char* const session_class_table[_SESSION_CLASS_MAX] = {
         [SESSION_USER]              = "user",
         [SESSION_USER_EARLY]        = "user-early",
         [SESSION_USER_INCOMPLETE]   = "user-incomplete",
+        [SESSION_USER_LIGHT]        = "user-light",
+        [SESSION_USER_EARLY_LIGHT]  = "user-early-light",
         [SESSION_GREETER]           = "greeter",
         [SESSION_LOCK_SCREEN]       = "lock-screen",
         [SESSION_BACKGROUND]        = "background",
index 53ac92e5d2045edc60eff76583563abb10486afe..c0cf03ff549603ad0bf0806f95d3cfebf6e927f0 100644 (file)
@@ -23,6 +23,8 @@ typedef enum SessionClass {
         SESSION_USER,               /* A regular user session */
         SESSION_USER_EARLY,         /* A user session, that is not ordered after systemd-user-sessions.service (i.e. for root) */
         SESSION_USER_INCOMPLETE,    /* A user session that is only half-way set up and doesn't pull in the service manager, and can be upgraded to a full user session later */
+        SESSION_USER_LIGHT,         /* Just like SESSION_USER, but doesn't pull in service manager */
+        SESSION_USER_EARLY_LIGHT,   /* Just like SESSION_USER_EARLY, but doesn't pull in service manager */
         SESSION_GREETER,            /* A login greeter pseudo-session */
         SESSION_LOCK_SCREEN,        /* A lock screen */
         SESSION_BACKGROUND,         /* Things like cron jobs, which are non-interactive */
@@ -36,10 +38,12 @@ typedef enum SessionClass {
 
 /* Whether we shall allow sessions of this class to run before 'systemd-user-sessions.service'. It's
  * generally set for root sessions, but no one else. */
-#define SESSION_CLASS_IS_EARLY(class) IN_SET((class), SESSION_USER_EARLY, SESSION_MANAGER_EARLY)
+#define SESSION_CLASS_IS_EARLY(class) IN_SET((class), SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_MANAGER_EARLY)
 
 /* Which session classes want their own scope units? (all of them, except the manager, which comes in its own service unit already */
-#define SESSION_CLASS_WANTS_SCOPE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_INCOMPLETE, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND, SESSION_BACKGROUND_LIGHT)
+#define SESSION_CLASS_WANTS_SCOPE(class) IN_SET((class),                \
+                                                SESSION_USER, SESSION_USER_EARLY, SESSION_USER_INCOMPLETE, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, \
+                                                SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND, SESSION_BACKGROUND_LIGHT)
 
 /* Which session classes want their own per-user service manager? */
 #define SESSION_CLASS_WANTS_SERVICE_MANAGER(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND)
@@ -48,25 +52,25 @@ typedef enum SessionClass {
 #define SESSION_CLASS_PIN_USER(class) (!IN_SET((class), SESSION_MANAGER, SESSION_MANAGER_EARLY, SESSION_NONE))
 
 /* Which session classes decide whether system is idle? (should only cover sessions that have input, and are not idle screens themselves)*/
-#define SESSION_CLASS_CAN_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
+#define SESSION_CLASS_CAN_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT, SESSION_GREETER))
 
 /* Which session classes have a lock screen concept? */
-#define SESSION_CLASS_CAN_LOCK(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
+#define SESSION_CLASS_CAN_LOCK(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT))
 
 /* Which sessions are candidates to become "display" sessions */
-#define SESSION_CLASS_CAN_DISPLAY(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
+#define SESSION_CLASS_CAN_DISPLAY(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT, SESSION_GREETER))
 
 /* Which sessions classes should be subject to stop-in-idle */
-#define SESSION_CLASS_CAN_STOP_ON_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
+#define SESSION_CLASS_CAN_STOP_ON_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT))
 
 /* Which session classes can take control of devices */
-#define SESSION_CLASS_CAN_TAKE_DEVICE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
+#define SESSION_CLASS_CAN_TAKE_DEVICE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, SESSION_GREETER, SESSION_LOCK_SCREEN))
 
 /* Which session classes allow changing session types */
-#define SESSION_CLASS_CAN_CHANGE_TYPE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
+#define SESSION_CLASS_CAN_CHANGE_TYPE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, SESSION_GREETER, SESSION_LOCK_SCREEN))
 
 /* Which session classes are taken into acccount when deciding whether shutdown shall be allowed if other users are logged in */
-#define SESSION_CLASS_IS_INHIBITOR_LIKE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY)
+#define SESSION_CLASS_IS_INHIBITOR_LIKE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT)
 
 typedef enum SessionType {
         SESSION_UNSPECIFIED,
index acb6bc3781f3b64d74c2089519e998afe50da95b..00fc2c360def47cc7e6a99b4626b1c0e59d28261 100644 (file)
@@ -1006,14 +1006,36 @@ static void session_context_mangle(
                 c->vtnr = 0;
         }
 
-        if (isempty(c->type))
+        if (isempty(c->type)) {
                 c->type = !isempty(c->display) ? "x11" :
                               !isempty(c->tty) ? "tty" : "unspecified";
+                pam_debug_syslog(handle, debug, "Automatically chose session type '%s'.", c->type);
+        }
+
+        if (isempty(c->class)) {
+                c->class = streq(c->type, "unspecified") ? "background" : "user";
+
+                /* For non-regular users tweak the type a bit:
+                 *
+                 * - Allow root tty logins *before* systemd-user-sessions.service is run, to allow early boot
+                 *   logins to debug things.
+                 *
+                 * - Non-graphical sessions shall be invoked without service manager.
+                 *
+                 * (Note that this somewhat replicates the class mangling logic on systemd-logind.service's
+                 * server side to some degree, in case clients allocate a session and don't specify a
+                 * class. This is somewhat redundant, but we need the class set up properly below.) */
+
+                if (IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC)) {
+                        if (streq(c->class, "user"))
+                                c->class = user_record_is_root(ur) ? "user-early" :
+                                        (STR_IN_SET(c->type, "x11", "wayland", "mir") ? "user" : "user-light");
+                        else if (streq(c->class, "background"))
+                                c->class = "background-light";
+                }
 
-        if (isempty(c->class))
-                c->class = streq(c->type, "unspecified") ? "background" :
-                        ((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) &&
-                         streq(c->type, "tty")) ? "user-early" : "user");
+                pam_debug_syslog(handle, debug, "Automatically chose session class '%s'.", c->class);
+        }
 
         if (c->incomplete) {
                 if (streq(c->class, "user"))
index 9076d474568ae1ddc6fd03381bff985f2b0279a0..f5c5664f66b02c6f3c7a125c7bfcc7bb15791c61 100644 (file)
@@ -21,6 +21,10 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
                 SD_VARLINK_DEFINE_ENUM_VALUE(user_early),
                 SD_VARLINK_FIELD_COMMENT("Regular user session whose home directory is not available right now, but will be later, at which point the session class can be upgraded to 'user'"),
                 SD_VARLINK_DEFINE_ENUM_VALUE(user_incomplete),
+                SD_VARLINK_FIELD_COMMENT("A user session that doesn't pull in the per-user service manager"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(user_light),
+                SD_VARLINK_FIELD_COMMENT("The combination of user_early and user_light"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(user_early_light),
                 SD_VARLINK_FIELD_COMMENT("Display manager greeter screen used for login"),
                 SD_VARLINK_DEFINE_ENUM_VALUE(greeter),
                 SD_VARLINK_FIELD_COMMENT("Similar, but a a lock screen"),
index 060e1fb18af661d4edbd7ceb2c4fb4b11e640d90..9a5c781001695aec063f0f5eabb427c496dbcacd 100755 (executable)
@@ -711,6 +711,8 @@ testcase_background() {
     TRANSIENTUNIT0="none$RANDOM.service"
     TRANSIENTUNIT1="bg$RANDOM.service"
     TRANSIENTUNIT2="bgg$RANDOM.service"
+    TRANSIENTUNIT3="bggg$RANDOM.service"
+    TRANSIENTUNIT4="bgggg$RANDOM.service"
 
     trap background_at_return RETURN
 
@@ -745,6 +747,17 @@ EOF
     systemctl stop "$TRANSIENTUNIT2"
 
     systemctl stop user@"$uid".service
+
+    # Now check that system users automatically get the light session class assigned
+    systemd-sysusers --inline "u lightuser"
+
+    systemd-run -u "$TRANSIENTUNIT3" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_TYPE=unspecified" -p Type=exec -p User=lightuser sleep infinity
+    loginctl | grep lightuser | grep -q background-light
+    systemctl stop "$TRANSIENTUNIT3"
+
+    systemd-run -u "$TRANSIENTUNIT4" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_TYPE=tty" -p Type=exec -p User=lightuser sleep infinity
+    loginctl | grep lightuser | grep -q user-light
+    systemctl stop "$TRANSIENTUNIT4"
 }
 
 testcase_varlink() {