summaryrefslogtreecommitdiff
path: root/src/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/misc')
-rw-r--r--src/misc/initgroups.c26
1 files changed, 22 insertions, 4 deletions
diff --git a/src/misc/initgroups.c b/src/misc/initgroups.c
index 922a9581..101f5c7b 100644
--- a/src/misc/initgroups.c
+++ b/src/misc/initgroups.c
@@ -1,11 +1,29 @@
#define _GNU_SOURCE
#include <grp.h>
#include <limits.h>
+#include <stdlib.h>
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[32], *groups = buf;
+ int count = sizeof buf / sizeof *buf, 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;
}