diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c --- a/usr.sbin/mountd/mountd.c +++ b/usr.sbin/mountd/mountd.c @@ -3613,13 +3613,15 @@ char *names; struct passwd *pw; struct group *gr; - gid_t groups[NGROUPS_MAX + 1]; - int ngroups; + long ngroups_max; + int alloc_ngroups; unsigned long name_ul; char *end = NULL; /* Start with 'cr_smallgrps' embedded storage. */ cr->cr_groups = cr->cr_smallgrps; + alloc_ngroups = SMALLNGROUPS; + _Static_assert(SMALLNGROUPS > 0, "SMALLNGROUPS must be > 0"); /* * Get the user's password table entry. @@ -3640,25 +3642,52 @@ cr->cr_uid = name_ul; } + /* How many groups do we support? */ + ngroups_max = sysconf(_SC_NGROUPS_MAX); + if (ngroups_max == -1) + ngroups_max = NGROUPS_MAX; + /* Add space for the effective GID. */ + ++ngroups_max; + /* * Credentials specified as those of a user. */ if (names == NULL) { + u_int retries = 5; + if (pw == NULL) { syslog(LOG_ERR, "unknown user: %s", name); goto fallback; } - ngroups = NGROUPS_MAX + 1; - if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) { - syslog(LOG_ERR, "too many groups"); - ngroups = NGROUPS_MAX + 1; + for (;;) { + if (retries == 0) { + syslog(LOG_ERR, "can't get stable group list " + "for user: %s", name); + if (cr->cr_groups != cr->cr_smallgrps) + free(cr->cr_groups); + goto fallback; + } + --retries; + cr->cr_ngroups = alloc_ngroups; + if (getgrouplist(pw->pw_name, pw->pw_gid, + cr->cr_groups, &cr->cr_ngroups) == 0) + break; + if (cr->cr_ngroups > ngroups_max) { + syslog(LOG_ERR, "too many groups"); + if (alloc_ngroups == ngroups_max) { + cr->cr_ngroups = ngroups_max; + break; + } + alloc_ngroups = ngroups_max; + } else + alloc_ngroups = cr->cr_ngroups; + cr->cr_groups = realloc( + cr->cr_groups == cr->cr_smallgrps ? NULL : + cr->cr_groups, alloc_ngroups); + if (cr->cr_groups == NULL) + out_of_mem(); } - - if (ngroups > SMALLNGROUPS) - cr->cr_groups = malloc(ngroups * sizeof(gid_t)); - cr->cr_ngroups = ngroups; - memcpy(cr->cr_groups, groups, ngroups * sizeof(gid_t)); return; } @@ -3681,11 +3710,31 @@ } else { group = name_ul; } - if (cr->cr_ngroups == NGROUPS_MAX) { + if (cr->cr_ngroups == ngroups_max) { syslog(LOG_ERR, "too many groups"); break; } - groups[cr->cr_ngroups++] = group; + ++cr->cr_ngroups; + /* Realloc if necessary. */ + if (cr->cr_ngroups > alloc_ngroups) { + if (cr->cr_groups == cr->cr_smallgrps) { + alloc_ngroups = 2 * SMALLNGROUPS; + cr->cr_groups = malloc(alloc_ngroups); + if (cr->cr_groups == NULL) + out_of_mem(); + memcpy(cr->cr_groups, cr->cr_smallgrps, + sizeof(cr->cr_smallgrps)); + } else { + alloc_ngroups = alloc_ngroups >= PAGE_SIZE ? + alloc_ngroups + PAGE_SIZE : + 2 * alloc_ngroups; + cr->cr_groups = realloc(cr->cr_groups, + alloc_ngroups); + if (cr->cr_groups == NULL) + out_of_mem(); + } + } + cr->cr_groups[cr->cr_ngroups - 1] = group; } if (cr->cr_ngroups == 0) { /* @@ -3696,7 +3745,7 @@ */ char *buf = NULL; /* Arbitrary initial size, should cover most practical cases. */ - size_t buf_size, next_size = 64; + size_t buf_size, next_size = 128; for (;;) { struct group *res, grp; @@ -3731,9 +3780,6 @@ */ cr->cr_ngroups = 1; } - if (cr->cr_ngroups > SMALLNGROUPS) - cr->cr_groups = malloc(cr->cr_ngroups * sizeof(gid_t)); - memcpy(cr->cr_groups, groups, cr->cr_ngroups * sizeof(gid_t)); return; fallback: @@ -3741,6 +3787,7 @@ * Set up the unprivileged user. */ cr->cr_uid = UID_NOBODY; + cr->cr_groups = cr->cr_smallgrps; cr->cr_groups[0] = GID_NOGROUP; cr->cr_ngroups = 1; }