diff --git a/ChangeLog b/ChangeLog index 18962e93a2..3f4fd80d42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2004-02-26 Ulrich Drepper + * sunrpc/svcauth_des.c (authdes_getucred): Removed fixed limit on + number of groups in the cache. Relax the disconnect between the + interface of authdes_getucred and netname2user a bit. + * sunrpc/auth_unix.c (authunix_create_default): Don't allocate + huge arrays on the stack. + * sysdeps/unix/sysv/linux/sysconf.c (__sysconf): Use the official not cancelable interfaces. 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 #include +#include #include #include #include @@ -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 #include #include #include @@ -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; }