diff options
author | Bernardo Meurer <bernardo@meurer.org> | 2022-06-08 13:45:39 -0400 |
---|---|---|
committer | Bernardo Meurer <bernardo@meurer.org> | 2022-06-08 13:45:41 -0400 |
commit | 931930feb139e6db0d7c01097003f8e45862f68f (patch) | |
tree | d6995540c4d352ca5c1fe7afcc19229c13655aff /src/libstore/lock.cc | |
parent | 12e86c0735987cbcc90b86122e9588fef4aa9ba5 (diff) |
fix(libstore/lock): support users that belong to more than 10 groups
The manpage for `getgrouplist` says:
> If the number of groups of which user is a member is less than or
> equal to *ngroups, then the value *ngroups is returned.
>
> If the user is a member of more than *ngroups groups, then
> getgrouplist() returns -1. In this case, the value returned in
> *ngroups can be used to resize the buffer passed to a further
> call getgrouplist().
In our original code, however, we allocated a list of size `10` and, if
`getgrouplist` returned `-1` threw an exception. In practice, this
caused the code to fail for any user belonging to more than 10 groups.
While unusual for single-user systems, large companies commonly have a
huge number of POSIX groups users belong to, causing this issue to crop
up and make multi-user Nix unusable in such settings.
The fix is relatively simple, when `getgrouplist` fails, it stores the
real number of GIDs in `ngroups`, so we must resize our list and retry.
Only then, if it errors once more, we can raise an exception.
This should be backported to, at least, 2.9.x.
Diffstat (limited to 'src/libstore/lock.cc')
-rw-r--r-- | src/libstore/lock.cc | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index f1356fdca..fa718f55d 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -67,13 +67,26 @@ bool UserLock::findFreeUser() { #if __linux__ /* Get the list of supplementary groups of this build user. This is usually either empty or contains a group such as "kvm". */ - supplementaryGIDs.resize(10); - int ngroups = supplementaryGIDs.size(); - int err = getgrouplist(pw->pw_name, pw->pw_gid, - supplementaryGIDs.data(), &ngroups); + int ngroups = 32; // arbitrary initial guess + supplementaryGIDs.resize(ngroups); + + int err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(), + &ngroups); + + // Our initial size of 32 wasn't sufficient, the correct size has + // been stored in ngroups, so we try again. + if (err == -1) { + supplementaryGIDs.resize(ngroups); + err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(), + &ngroups); + } + + // If it failed once more, then something must be broken. if (err == -1) - throw Error("failed to get list of supplementary groups for '%1%'", pw->pw_name); + throw Error("failed to get list of supplementary groups for '%1%'", + pw->pw_name); + // Finally, trim back the GID list to its real size supplementaryGIDs.resize(ngroups); #endif |