aboutsummaryrefslogtreecommitdiff
path: root/sunrpc
diff options
context:
space:
mode:
Diffstat (limited to 'sunrpc')
-rw-r--r--sunrpc/auth_unix.c58
-rw-r--r--sunrpc/svcauth_des.c58
2 files changed, 91 insertions, 25 deletions
diff --git a/sunrpc/auth_unix.c b/sunrpc/auth_unix.c
index 8c1134fba4..1cf18cb6b3 100644
--- a/sunrpc/auth_unix.c
+++ b/sunrpc/auth_unix.c
@@ -38,7 +38,9 @@
* for the credentials.
*/
+#include <errno.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
@@ -165,25 +167,61 @@ INTDEF (authunix_create)
AUTH *
authunix_create_default (void)
{
- int len;
char machname[MAX_MACHINE_NAME + 1];
- uid_t uid;
- gid_t gid;
- int max_nr_groups = __sysconf (_SC_NGROUPS_MAX);
- gid_t gids[max_nr_groups];
if (__gethostname (machname, MAX_MACHINE_NAME) == -1)
abort ();
machname[MAX_MACHINE_NAME] = 0;
- uid = __geteuid ();
- gid = __getegid ();
+ uid_t uid = __geteuid ();
+ gid_t gid = __getegid ();
+
+ int max_nr_groups;
+ /* When we have to try a second time, do not use alloca() again. We
+ might have reached the stack limit already. */
+ bool retry = false;
+ again:
+ /* Ask the kernel how many groups there are exactly. Note that we
+ might have to redo all this if the number of groups has changed
+ between the two calls. */
+ max_nr_groups = __getgroups (0, NULL);
+
+ /* Just some random reasonable stack limit. */
+#define ALLOCA_LIMIT (1024 / sizeof (gid_t))
+ gid_t *gids = NULL;
+ if (max_nr_groups < ALLOCA_LIMIT && ! retry)
+ gids = (gid_t *) alloca (max_nr_groups * sizeof (gid_t));
+ else
+ {
+ gids = (gid_t *) malloc (max_nr_groups * sizeof (gid_t));
+ if (gids == NULL)
+ return NULL;
+ }
+
+ int len = __getgroups (max_nr_groups, gids);
+ if (len == -1)
+ {
+ if (errno == EINVAL)
+ {
+ /* New groups added in the meantime. Try again. */
+ if (max_nr_groups >= ALLOCA_LIMIT || retry)
+ free (gids);
+ retry = true;
+ goto again;
+ }
+ /* No other error can happen. */
+ abort ();
+ }
- if ((len = __getgroups (max_nr_groups, gids)) < 0)
- abort ();
/* This braindamaged Sun code forces us here to truncate the
list of groups to NGRPS members since the code in
authuxprot.c transforms a fixed array. Grrr. */
- return INTUSE(authunix_create) (machname, uid, gid, MIN (NGRPS, len), gids);
+ AUTH *result = INTUSE(authunix_create) (machname, uid, gid, MIN (NGRPS, len),
+ gids);
+
+ if (max_nr_groups >= ALLOCA_LIMIT || retry)
+ free (gids);
+
+ return result;
}
INTDEF (authunix_create_default)
diff --git a/sunrpc/svcauth_des.c b/sunrpc/svcauth_des.c
index d808e95f05..07d7bd0122 100644
--- a/sunrpc/svcauth_des.c
+++ b/sunrpc/svcauth_des.c
@@ -43,6 +43,7 @@
*
*/
+#include <limits.h>
#include <string.h>
#include <sys/param.h>
#include <netinet/in.h>
@@ -487,8 +488,9 @@ struct bsdcred
{
uid_t uid; /* cached uid */
gid_t gid; /* cached gid */
- short grouplen; /* length of cached groups */
- gid_t groups[NGROUPS]; /* cached groups */
+ int grouplen; /* length of cached groups */
+ int grouplen_max; /* length of allocated cached groups */
+ gid_t groups[0]; /* cached groups */
};
/*
@@ -515,13 +517,7 @@ authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid,
return 0;
}
cred = (struct bsdcred *) authdes_cache[sid].localcred;
- if (cred == NULL)
- {
- cred = (struct bsdcred *) mem_alloc (sizeof (struct bsdcred));
- authdes_cache[sid].localcred = (char *) cred;
- cred->grouplen = INVALID;
- }
- if (cred->grouplen == INVALID)
+ if (cred == NULL || cred->grouplen == INVALID)
{
/*
* not in cache: lookup
@@ -530,15 +526,43 @@ authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid,
&i_grouplen, groups))
{
debug ("unknown netname");
- cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */
+ if (cred != NULL)
+ cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */
return 0;
}
+
+ if (cred != NULL && cred->grouplen_max < i_grouplen)
+ {
+ /* We already have an allocated data structure. But it is
+ too small. */
+ free (cred);
+ authdes_cache[sid].localcred = NULL;
+ cred = NULL;
+ }
+
+ if (cred == NULL)
+ {
+ /* We should allocate room for at least NGROUPS groups. */
+ int ngroups_max = MAX (i_grouplen, NGROUPS);
+
+ cred = (struct bsdcred *) mem_alloc (sizeof (struct bsdcred)
+ + ngroups_max * sizeof (gid_t));
+ if (cred == NULL)
+ return 0;
+
+ authdes_cache[sid].localcred = (char *) cred;
+ cred->grouplen = INVALID;
+ cred->grouplen_max = ngroups_max;
+ }
+
debug ("missed ucred cache");
*uid = cred->uid = i_uid;
*gid = cred->gid = i_gid;
- *grouplen = cred->grouplen = i_grouplen;
+ cred->grouplen = i_grouplen;
for (i = i_grouplen - 1; i >= 0; --i)
- cred->groups[i] = groups[i]; /* int to short */
+ cred->groups[i] = groups[i];
+ /* Make sure no too large values are reported. */
+ *grouplen = MIN (SHRT_MAX, i_grouplen);
return 1;
}
else if (cred->grouplen == UNKNOWN)
@@ -554,9 +578,13 @@ authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid,
*/
*uid = cred->uid;
*gid = cred->gid;
- *grouplen = cred->grouplen;
- for (i = cred->grouplen - 1; i >= 0; --i)
- groups[i] = cred->groups[i]; /* short to int */
+
+ /* Another stupidity in the interface: *grouplen is of type short.
+ So we might have to cut the information passed up short. */
+ int grouplen_copy = MIN (SHRT_MAX, cred->grouplen);
+ *grouplen = grouplen_copy;
+ for (i = grouplen_copy - 1; i >= 0; --i)
+ groups[i] = cred->groups[i];
return 1;
}