about summary refs log tree commit diff
path: root/sunrpc/svcauth_des.c
diff options
context:
space:
mode:
Diffstat (limited to 'sunrpc/svcauth_des.c')
-rw-r--r--sunrpc/svcauth_des.c58
1 files changed, 43 insertions, 15 deletions
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;
 }