systemd icon indicating copy to clipboard operation
systemd copied to clipboard

sysusers: handle soft-static allocations in a way that is not influenced by line order

Open mosvald opened this issue 7 months ago • 6 comments

systemd version the issue has been seen with

257.5

Used distribution

Fedora 43

Linux kernel version used

No response

CPU architectures issue was seen on

None

Component

No response

Expected behaviour you didn't see

 ~]# systemd-sysusers ./cyrus-sasl.conf
Creating group 'saslauth' with GID 76.
Creating user 'saslauth' (Saslauthd user) with UID 989 and GID 76.
 ~]#

 ~]# systemd-sysusers ./cyrus-imapd.conf
Creating user 'cyrus' (Cyrus IMAP Server) with UID 76 and GID 12.
 ~]#

Unexpected behaviour you saw

 ~]# systemd-sysusers ./cyrus-sasl.conf
Creating group 'saslauth' with GID 76.
Creating user 'saslauth' (Saslauthd user) with UID 76 and GID 76.
 ~]#

 ~]# systemd-sysusers ./cyrus-imapd.conf
Suggested user ID 76 for cyrus already used.
Creating user 'cyrus' (Cyrus IMAP Server) with UID 989 and GID 12.
 ~]#

Steps to reproduce the problem

cyrus-sasl.conf:

#Type Name       ID     GECOS              Home directory   Shell
g     saslauth   76
u     saslauth   -      "Saslauthd user"   /run/saslauthd   /sbin/nologin
m     saslauth   saslauth

cyrus-imapd.conf:

#Type Name     ID             GECOS                 Home directory Shell
g     saslauth 76
u     cyrus    76:mail        "Cyrus IMAP Server"   /var/lib/imap  /sbin/nologin
m     cyrus    saslauth

run: ~]# systemd-sysusers ./cyrus-sasl.conf ~]# systemd-sysusers ./cyrus-imapd.conf

Additional program output to the terminal or log subsystem illustrating the issue


mosvald avatar May 16 '25 14:05 mosvald

It shouldn't execute the following code in add_user():

1245         /* Otherwise, try to reuse the group ID */
1246         if (!i->uid_set && i->gid_set) {
1247                 r = uid_is_ok(c, (uid_t) i->gid, i->name, true);
1248                 if (r < 0)
1249                         return log_error_errno(r, "Failed to verify UID " UID_FMT ": %m", i->uid);
1250                 if (r > 0) {
1251                         i->uid = (uid_t) i->gid;
1252                         i->uid_set = true;
1253                 }
1254         }

but try to find the free one instead:

1256         /* And if that didn't work either, let's try to find a free one */
1257         if (!i->uid_set) {
1258                 maybe_emit_login_defs_warning(c);
1259 
1260                 for (;;) {
1261                         r = uid_range_next_lower(c->uid_range, &c->search_uid);
...

The above breaks the situation where 2 distinct daemons (2 distinct packages) share the same GID, but only one of them has UID equal GID and this UID is reserved for it.

For example cyrus-sasl and cyrus-imapd.

Cyrus-sasl has the following in its sysusers config (only GID=76 defined):

#Type Name       ID     GECOS              Home directory   Shell
g     saslauth   76
u     saslauth   -      "Saslauthd user"   /run/saslauthd   /sbin/nologin
m     saslauth   saslauth

Cyrus-imapd has this (UID=GID=76 set):

#Type Name     ID             GECOS                 Home directory Shell
g     saslauth 76
u     cyrus    76:mail        "Cyrus IMAP Server"   /var/lib/imap  /sbin/nologin
m     cyrus    saslauth

Cyrus-imapd has a reserved UID (76) for it, but cyrus-sasl doesn't:

 ~]# grep -w 76 /usr/share/doc/setup/uidgid
cyrus   76      (12)    /var/imap               /bin/bash       cyrus-imapd
saslauth        -       76      -               -       cyrus-sasl, cyrus-imap
 ~]#

If curus-sasl is installed before cyrus-imapd, saslauth takes 76 as its UID making the cyrus user dynamically allocated instead of being static across all systems:

 ~]# systemd-sysusers ./cyrus-sasl.conf
Creating group 'saslauth' with GID 76.
Creating user 'saslauth' (Saslauthd user) with UID 76 and GID 76.
 ~]#

 ~]# systemd-sysusers ./cyrus-imapd.conf
Suggested user ID 76 for cyrus already used.
Creating user 'cyrus' (Cyrus IMAP Server) with UID 989 and GID 12.
 ~]#

mosvald avatar May 16 '25 14:05 mosvald

Hmm, maybe better to introduce a knob to acquire a uid in dynamic range even if there exists a group with the same name. E.g.

u     saslauth   (dynamic)      "Saslauthd user"   /run/saslauthd   /sbin/nologin

yuwata avatar May 16 '25 14:05 yuwata

We do not support schemes where two records share the same id. it's a broken concept, because reverse lookups won't work.

The UID you specify is mostly a hint, we'll refuse to allocate it if it is already used, for robustness, to avoid creating ownership conflicts.

I guess we can document this better, but avoiding conflicts is definitely the better approach than accepting them, and opening up the system to vulnerabilities because distinct groups suddenly open up access to each other's resources.

poettering avatar May 17 '25 09:05 poettering

@poettering This shouldn't be a hint. The current behaviour leads to breaking the concept of soft static allocation on Fedora.:

https://docs.fedoraproject.org/en-US/packaging-guidelines/UsersAndGroups/#_soft_static_allocation

If there are pre-allocated UIDs/GIDs systemd-sysusers shouldn't decide by itself which one to allocate to which service and leave the other service to get a random UID.

'-' should allocate a higher number and not allocate from the soft static allocation pool.

cc: @keszybz

mosvald avatar Jun 16 '25 11:06 mosvald

sorry, but sharing uids/gids between multiple records is not something we are going to support. that's just broken. I don't think that the fedora policy condones that, why you seem to imply with that link?

poettering avatar Jun 16 '25 11:06 poettering

sorry, but sharing uids/gids between multiple records is not something we are going to support. that's just broken. I don't think that the fedora policy condones that, why you seem to imply with that link?

Nah, this is a misunderstanding. No uid/gid sharing is happening. Instead, the request is to have a setup where the uid and gid used for the user and group with the same name are different.

~]# systemd-sysusers ./cyrus-sasl.conf Creating group 'saslauth' with GID 76. Creating user 'saslauth' (Saslauthd user) with UID 76 and GID 76.

~]# systemd-sysusers ./cyrus-imapd.conf Suggested user ID 76 for cyrus already used. Creating user 'cyrus' (Cyrus IMAP Server) with UID 989 and GID 12.

The issue is that when the only the first file is available, systemd-sysusers has no way to know that UID 76 should not be used for saslauth user. systemd-sysusers has the general approach that the uid and gid should be the same, so when instructed to create a group with a fixed gid and a user with no gid specified, it'll use the same number for both.

The easiest way to solve this is to make make sure the full config is available to sysusers in all installation scenarios. This can be solved downstream by rearranging the config.


Unfortunately, systemd-sysusers seems to grab the uid/gid numbers immediately when reading the config, instead of reading the config first, figuring out all the fixed uids/gids, and then doing the assignments later. This makes the effect sensitive to declaration order:

$ build/systemd-sysusers --root /tmp/empty/ --dry-run --inline 'g groupa -' 'g groupb 999'
Creating group 'groupa' with GID 999.
Suggested group ID 999 for groupb already used.
Creating group 'groupb' with GID 998.

$ build/systemd-sysusers --root /tmp/empty/ --dry-run --inline 'g groupb 999' 'g groupa -'
Creating group 'groupb' with GID 999.
Creating group 'groupa' with GID 998.

In the more complicated case of saslauth/cyrus above, this works:

$ build/systemd-sysusers --root /var/tmp/inst8/ --dry-run --inline 'g saslauth 76' 'g mail 12' 'u cyrus 76:mail' 'u saslauth -'
Creating group 'saslauth' with GID 76.
Creating group 'mail' with GID 12.
Creating user 'cyrus' (n/a) with UID 76 and GID 12.
Creating user 'saslauth' (n/a) with UID 999 and GID 76.

but this doesn't:

$ build/systemd-sysusers --root /var/tmp/inst8/ --dry-run --inline 'g saslauth 76' 'g mail 12' 'u saslauth -' 'u cyrus 76:mail'
Creating group 'saslauth' with GID 76.
Creating group 'mail' with GID 12.
Creating user 'saslauth' (n/a) with UID 76 and GID 76.
Suggested user ID 76 for cyrus already used.
Creating user 'cyrus' (n/a) with UID 999 and GID 12.

Things are easier to configure and more robust if line order doesn't matter. I think we should change the code to handle this better.

keszybz avatar Jun 16 '25 13:06 keszybz