diff --git a/NEWS b/NEWS index dde530e3964..b6b1c809072 100644 --- 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 48ab8eac84c..81d8151cae3 100644 --- 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. diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 18c3636b6bf..1093df9f82d 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -115,6 +115,14 @@ user-early Similar to user but sessions of this class are not ordered after systemd-user-sessions.service8, 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 user class, see above. (Added in v256.) + + user-light + Similar to user, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v258.) + + + user-early-light + Similar to user-early, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v258.) + user-incomplete Similar to user but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by systemd-homed.service8 to allow users to log in via ssh1 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 user class once the home directory is activated. @@ -133,7 +141,7 @@ background-light - Similar to background, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no services of the user running. (Added in v256.) + Similar to background, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v256.) manager diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml index 1cbef64e003..92cd669dc48 100644 --- a/man/sd_session_is_active.xml +++ b/man/sd_session_is_active.xml @@ -216,10 +216,10 @@ sd_session_get_class() may be used to determine the class of the session identified by the specified session identifier. The returned string is one of user, - user-early, user-incomplete, greeter, - lock-screen, background, background-light, - manager or manager-early and needs to be freed with the libc - user-early, user-light, user-early-light, + user-incomplete, greeter, lock-screen, + background, background-light, manager or + manager-early and needs to be freed with the libc free3 call after use. diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ab39cc0644b..5d3e2e01b50 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -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; diff --git a/src/login/logind-session.c b/src/login/logind-session.c index bc48609c26c..686041320f3 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -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", diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 53ac92e5d20..c0cf03ff549 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -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, diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index acb6bc3781f..00fc2c360de 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -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" : - ((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) && - streq(c->type, "tty")) ? "user-early" : "user"); + 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"; + } + + pam_debug_syslog(handle, debug, "Automatically chose session class '%s'.", c->class); + } if (c->incomplete) { if (streq(c->class, "user")) diff --git a/src/shared/varlink-io.systemd.Login.c b/src/shared/varlink-io.systemd.Login.c index 9076d474568..f5c5664f66b 100644 --- a/src/shared/varlink-io.systemd.Login.c +++ b/src/shared/varlink-io.systemd.Login.c @@ -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"), diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh index 060e1fb18af..9a5c7810016 100755 --- a/test/units/TEST-35-LOGIN.sh +++ b/test/units/TEST-35-LOGIN.sh @@ -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() {