From 554130faf3525691d9443a61aebf3b16e5c44a87 Mon Sep 17 00:00:00 2001 From: Adrian Vovk Date: Wed, 21 May 2025 17:32:03 -0400 Subject: [PATCH] Define uid range for greeter In multi-seat scenarios, a display manager might need to start multiple greeter sessions. But systemd allows at most one graphical session per user. So, display managers now have a range of UIDs to dynamically allocate users for their greeter sessions. --- docs/UIDS-GIDS.md | 26 +++++++++++++++++++++----- meson.build | 6 ++++++ meson_options.txt | 4 ++++ src/basic/uid-classification.c | 2 +- src/basic/uid-classification.h | 4 ++++ src/core/systemd.pc.in | 3 +++ src/coredump/coredump.c | 2 +- src/dissect/dissect.c | 2 +- src/home/homectl.c | 2 ++ src/home/homed-home.c | 3 ++- src/shared/user-record.c | 2 +- 11 files changed, 46 insertions(+), 10 deletions(-) diff --git a/docs/UIDS-GIDS.md b/docs/UIDS-GIDS.md index 9fd3dc5007..d9e38b2b70 100644 --- a/docs/UIDS-GIDS.md +++ b/docs/UIDS-GIDS.md @@ -100,7 +100,18 @@ possible. attempted to make UID assignments stable, by deriving them from a hash of the user name. -2. 61184…65519 → UIDs for dynamic users are allocated from this range (see the +2. 60578…60705 → UIDs for dynamic greeter users are allocated from this range. + In multiseat scenarios, multiple greeter sessions may be running at once. + However, systemd only permits one graphical session at a time per user + ([documentation](/DESKTOP_ENVIRONMENTS)). Thus, multiseat-enabled display + managers (like GDM) must run each greeter session under a unique user. To + make use of this UID range, the display manager should implement the + [userdb Varlink API](/USER_GROUP_API) and dynamically allocate users whenever + they are needed by the display manager. Display managers may also use these + UIDs for other purposes where dynamic users may be helpful (i.e. guest user + sessions or kiosk sessions) + +3. 61184…65519 → UIDs for dynamic users are allocated from this range (see the `DynamicUser=` documentation in [`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html)). This range has been chosen so that it is below the 16-bit boundary @@ -114,7 +125,7 @@ possible. for all currently allocated dynamic users from this range. Thus, NSS-based user record resolving works correctly without those users being in `/etc/passwd`. -3. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of +4. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of per-container UID ranges. When the `--private-users=pick` switch is used (or `-U`) then it will automatically find a so far unused 16-bit subrange of this range and assign it to the container. @@ -129,7 +140,7 @@ possible. erroneously considers UIDs signed integers, and hence can't deal with values above 2^31. The `systemd-machined.service` service will synthesize user database records for all UIDs assigned to a running container from this range. -4. 2147352576…2147418111 → UID range used for foreign OS images. For various +5. 2147352576…2147418111 → UID range used for foreign OS images. For various usecases (primarily: containers) it makes sense to make foreign OS images available locally whose UID/GID ownerships do not make sense in the local context but only within the OS image itself. This 64K UID range can be used @@ -157,6 +168,10 @@ The most important boundaries of the local system may be queried with ```sh $ pkg-config --variable=system_uid_max systemd 999 +$ pkg-config --variable=greeter_uid_min systemd +60578 +$ pkg-config --variable=greeter_uid_max systemd +60705 $ pkg-config --variable=dynamic_uid_min systemd 61184 $ pkg-config --variable=dynamic_uid_max systemd @@ -263,7 +278,8 @@ i.e. somewhere below `/var/` or similar. | 1000…60000 | 0x000003E8…0x00001770 | 59000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… | | 60001…60513 | 0x0000EA61…0x0000EC61 | 513 | Human users (homed) | `systemd` | `nss-systemd` | | 60514…60577 | 0x0000EC62…0x0000ECA1 | 64 | Host users mapped into containers | `systemd` | `systemd-nspawn` | -| 60578…61183 | 0x0000ECA2…0x0000EEFF | 606 | *unused* | | | +| 60578…60705 | 0x0000ECA2…0x0000ED21 | 128 | Dynamic greeter users | `systemd` | `nss-systemd` | +| 60706…61183 | 0x0000ED22…0x0000EEFF | 478 | *unused* | | | | 61184…65519 | 0x0000EF00…0x0000FFEF | 4336 | Dynamic service users | `systemd` | `nss-systemd` | | 65520…65533 | 0x0000FFF0…0x0000FFFD | 13 | *unused* | | | | 65534 | 0x0000FFFE | 1 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` | @@ -279,7 +295,7 @@ i.e. somewhere below `/var/` or similar. Note that "Unused" in the table above doesn't mean that these ranges are really unused. It just means that these ranges have no well-established pre-defined purposes between Linux, generic low-level distributions and `systemd`. -There might very well be other packages that allocate from theseranges. +There might very well be other packages that allocate from these ranges. Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled with care. Various programs (including kernel file systems — see `devpts` — or diff --git a/meson.build b/meson.build index 63eac45bbb..f8e7fcade2 100644 --- a/meson.build +++ b/meson.build @@ -810,6 +810,11 @@ if conf.get('SYSTEM_ALLOC_GID_MIN') >= conf.get('SYSTEM_GID_MAX') error('Invalid gid allocation range') endif +greeter_uid_min = get_option('greeter-uid-min') +greeter_uid_max = get_option('greeter-uid-max') +conf.set('GREETER_UID_MIN', greeter_uid_min) +conf.set('GREETER_UID_MAX', greeter_uid_max) + dynamic_uid_min = get_option('dynamic-uid-min') dynamic_uid_max = get_option('dynamic-uid-max') conf.set('DYNAMIC_UID_MIN', dynamic_uid_min) @@ -2951,6 +2956,7 @@ summary({ conf.get('SYSTEM_ALLOC_UID_MIN')), 'system GIDs' : '<=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'), conf.get('SYSTEM_ALLOC_GID_MIN')), + 'greeter UIDs' : '@0@…@1@'.format(greeter_uid_min, greeter_uid_max), 'dynamic UIDs' : '@0@…@1@'.format(dynamic_uid_min, dynamic_uid_max), 'container UID bases' : '@0@…@1@'.format(container_uid_base_min, container_uid_base_max), 'foreign UID base' : '@0@'.format(foreign_uid_base), diff --git a/meson_options.txt b/meson_options.txt index 47c98631da..8d62f5157c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -256,6 +256,10 @@ option('system-uid-max', type : 'integer', value : 0, description : 'maximum system UID') option('system-gid-max', type : 'integer', value : 0, description : 'maximum system GID') +option('greeter-uid-min', type : 'integer', value : 0x0000ECA2, + description : 'minimum greeter UID') +option('greeter-uid-max', type : 'integer', value : 0x0000ED21, + description : 'maximum greeter UID') option('dynamic-uid-min', type : 'integer', value : 0x0000EF00, description : 'minimum dynamic UID') option('dynamic-uid-max', type : 'integer', value : 0x0000FFEF, diff --git a/src/basic/uid-classification.c b/src/basic/uid-classification.c index 91ea1f1167..203ce2c68a 100644 --- a/src/basic/uid-classification.c +++ b/src/basic/uid-classification.c @@ -129,5 +129,5 @@ bool uid_for_system_journal(uid_t uid) { /* Returns true if the specified UID shall get its data stored in the system journal. */ - return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY || uid_is_container(uid) || uid_is_foreign(uid); + return uid_is_system(uid) || uid_is_dynamic(uid) || uid_is_greeter(uid) || uid == UID_NOBODY || uid_is_container(uid) || uid_is_foreign(uid); } diff --git a/src/basic/uid-classification.h b/src/basic/uid-classification.h index bd87ffc80d..f732b6ef9b 100644 --- a/src/basic/uid-classification.h +++ b/src/basic/uid-classification.h @@ -18,6 +18,10 @@ assert_cc((FOREIGN_UID_BASE & 0xFFFFU) == 0); bool uid_is_system(uid_t uid); bool gid_is_system(gid_t gid); +static inline bool uid_is_greeter(uid_t uid) { + return GREETER_UID_MIN <= uid && uid <= GREETER_UID_MAX; +} + static inline bool uid_is_dynamic(uid_t uid) { return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; } diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in index 8d044dd7ad..2b5fb8a2ec 100644 --- a/src/core/systemd.pc.in +++ b/src/core/systemd.pc.in @@ -92,6 +92,9 @@ systemuidmax=${system_uid_max} system_gid_max={{SYSTEM_GID_MAX}} systemgidmax=${system_gid_max} +greeter_uid_min={{GREETER_UID_MIN}} +greeter_uid_max={{GREETER_UID_MAX}} + dynamic_uid_min={{DYNAMIC_UID_MIN}} dynamicuidmin=${dynamic_uid_min} dynamic_uid_max={{DYNAMIC_UID_MAX}} diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 55a0b704e3..ff5c933dea 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -260,7 +260,7 @@ static int fix_acl(int fd, uid_t uid, bool allow_user) { if (!allow_user) return 0; - if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + if (uid_is_system(uid) || uid_is_dynamic(uid) || uid_is_greeter(uid) || uid == UID_NOBODY) return 0; /* Make sure normal users can read (but not write or delete) their own coredumps */ diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 14319413f0..7b78a46d25 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -1293,7 +1293,7 @@ static const char *pick_color_for_uid_gid(uid_t uid) { return ansi_highlight_yellow4(); /* files should never be owned by 'nobody' (but might happen due to userns mapping) */ if (uid_is_system(uid)) return ansi_normal(); /* files in disk images are typically owned by root and other system users, no issue there */ - if (uid_is_dynamic(uid)) + if (uid_is_dynamic(uid) || uid_is_greeter(uid)) return ansi_highlight_red(); /* files should never be owned persistently by dynamic users, and there are just no excuses */ if (uid_is_container(uid) || uid_is_foreign(uid)) return ansi_highlight_cyan(); diff --git a/src/home/homectl.c b/src/home/homectl.c index a015f8edfe..f7420708bf 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -3719,6 +3719,8 @@ static int parse_argv(int argc, char *argv[]) { if (uid_is_system(uid)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid); + if (uid_is_greeter(uid)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in greeter range, refusing.", uid); if (uid_is_dynamic(uid)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid); if (uid == UID_NOBODY) diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 2d292163d0..244e10166d 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -86,7 +86,8 @@ static int suitable_home_record(UserRecord *hr) { /* Insist we are outside of the dynamic and system range */ if (uid_is_system(hr->uid) || gid_is_system(user_record_gid(hr)) || - uid_is_dynamic(hr->uid) || gid_is_dynamic(user_record_gid(hr))) + uid_is_dynamic(hr->uid) || gid_is_dynamic(user_record_gid(hr)) || + uid_is_greeter(hr->uid)) return -EADDRNOTAVAIL; /* Insist that GID and UID match */ diff --git a/src/shared/user-record.c b/src/shared/user-record.c index e0ff097bf3..20f5d92246 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -2070,7 +2070,7 @@ UserDisposition user_record_disposition(UserRecord *h) { if (uid_is_system(h->uid)) return USER_SYSTEM; - if (uid_is_dynamic(h->uid)) + if (uid_is_dynamic(h->uid) || uid_is_greeter(h->uid)) return USER_DYNAMIC; if (uid_is_container(h->uid))