]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Define uid range for greeter
authorAdrian Vovk <adrianvovk@gmail.com>
Wed, 21 May 2025 21:32:03 +0000 (17:32 -0400)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 2 Jun 2025 09:19:12 +0000 (11:19 +0200)
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
meson.build
meson_options.txt
src/basic/uid-classification.c
src/basic/uid-classification.h
src/core/systemd.pc.in
src/coredump/coredump.c
src/dissect/dissect.c
src/home/homectl.c
src/home/homed-home.c
src/shared/user-record.c

index 9fd3dc500749dd8c887908ccc498adf886231281..d9e38b2b70a261d244a4d31e390a97dfa8df22f1 100644 (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
index 63eac45bbba7ad10b28801c2bb405e9b6e5207f3..f8e7fcade2b33f1e64f1dfcd51fd5c98d1d2e62e 100644 (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),
index 47c98631da4ec8279d2ba205ed31f60fd11b2442..8d62f5157cab29599a0e1f5f97fa1dd77761c870 100644 (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,
index 91ea1f1167dfe745112eb5bcf55a9cb3b3abd3b8..203ce2c68a81f2455b3a69426918a1b4646b2645 100644 (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);
 }
index bd87ffc80d7d889dfa43e4669f4331c0d75d1e5c..f732b6ef9b58fb2e1f75450b104fb1faf6e1ec7d 100644 (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;
 }
index 8d044dd7ad1f116a9a4e560fa4d42fa0023472ce..2b5fb8a2ece0dcb0afaa9d06eba34513330c6bdc 100644 (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}}
index 55a0b704e3de2da31e6e428920b4a75889ec6f75..ff5c933dea50ba24e2724fd3ccbd2247da1845b1 100644 (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 */
index 14319413f0c9569f5d970626aad17bf540674183..7b78a46d2514afb999eb8756b32cd54dcbe36a25 100644 (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();
index a015f8edfe409424fc3fee6776c616e62a84190e..f7420708bf379b1f872fe815014fc1c36444bc0e 100644 (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)
index 2d292163d0974b791eec0f958ec889ba63c5941e..244e10166dcbb42a783d8d371c9522e2bc998bf0 100644 (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 */
index e0ff097bf30d26e99eee3befa56edc28d78df183..20f5d9224648c10da22dc87f9de4bbe79f577919 100644 (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))