Update.
authorUlrich Drepper <drepper@redhat.com>
Thu, 26 Feb 2004 19:22:02 +0000 (19:22 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 26 Feb 2004 19:22:02 +0000 (19:22 +0000)
* 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.

ChangeLog
sunrpc/auth_unix.c
sunrpc/svcauth_des.c

index 18962e9..3f4fd80 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2004-02-26  Ulrich Drepper  <drepper@redhat.com>
 
+       * 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.
 
index 8c1134f..1cf18cb 100644 (file)
@@ -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)
 
index d808e95..07d7bd0 100644 (file)
@@ -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;
 }