diff --git a/src/misc/initgroups.c b/src/misc/initgroups.c index 922a9581..d7138976 100644 --- a/src/misc/initgroups.c +++ b/src/misc/initgroups.c @@ -1,11 +1,34 @@ #define _GNU_SOURCE #include #include +#include +#include +#include + +#undef NGROUPS_MAX +#define NGROUPS_MAX 2 int initgroups(const char *user, gid_t gid) { - gid_t groups[NGROUPS_MAX]; - int count = NGROUPS_MAX; - if (getgrouplist(user, gid, groups, &count) < 0) return -1; - return setgroups(count, groups); + gid_t buf[NGROUPS_MAX], *groups = buf; + int count = NGROUPS_MAX, prev_count = count; + while (getgrouplist(user, gid, groups, &count) < 0) { + if (groups != buf) free(groups); + + /* Return if failure isn't buffer size */ + if (count <= prev_count) + return -1; + + /* Always increase by at least 50% to limit to + * logarithmically many retries on TOCTOU races. */ + if (count < prev_count + (prev_count>>1)) + count = prev_count + (prev_count>>1); + + groups = calloc(count, sizeof *groups); + if (!groups) return -1; + prev_count = count; + } + int ret = setgroups(count, groups); + if (groups != buf) free(groups); + return ret; }