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.
This commit is contained in:
Adrian Vovk
2025-05-21 17:32:03 -04:00
committed by Zbigniew Jędrzejewski-Szmek
parent 2dcf0ee0ef
commit 554130faf3
11 changed files with 46 additions and 10 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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}}

View File

@@ -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 */

View File

@@ -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();

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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))