cleanup spec
[platform/upstream/glibc.git] / sunrpc / svcauth_des.c
index c74e06b..a0f961d 100644 (file)
@@ -1,41 +1,33 @@
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)svcauth_des.c      2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI";
-#endif
-
-/*
- * Copyright (c) 1988 by Sun Microsystems, Inc.
- */
-
 /*
- * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
- * unrestricted use provided that this legend is included on all tape
- * media and as a part of the software program in whole or part.  Users
- * may copy or modify Sun RPC without charge, but are not authorized
- * to license or distribute it to anyone else except as part of a product or
- * program developed by the user.
- *
- * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
- * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * Copyright (c) 2010, Oracle America, Inc.
  *
- * Sun RPC is provided with no support and without any obligation on the
- * part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
  *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
- * OR ANY PART THEREOF.
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *     * Neither the name of the "Oracle America, Inc." nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
  *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California  94043
- */
-
-/*
  * svcauth_des.c, server-side des authentication
  *
  * We insure for the service the following:
@@ -49,10 +41,12 @@ static char sccsid[] = "@(#)svcauth_des.c   2.3 89/07/11 4.0 RPCSRC; from 1.15 88/
  *
  */
 
+#include <limits.h>
 #include <string.h>
+#include <stdint.h>
 #include <sys/param.h>
 #include <netinet/in.h>
-#include <rpc/types.h>
+#include <rpc/rpc.h>
 #include <rpc/xdr.h>
 #include <rpc/auth.h>
 #include <rpc/auth_des.h>
@@ -62,7 +56,7 @@ static char sccsid[] = "@(#)svcauth_des.c     2.3 89/07/11 4.0 RPCSRC; from 1.15 88/
 
 #define debug(msg)             /*printf("svcauth_des: %s\n", msg) */
 
-#define USEC_PER_SEC ((u_long) 1000000L)
+#define USEC_PER_SEC ((uint32_t) 1000000L)
 #define BEFORE(t1, t2) timercmp(t1, t2, <)
 
 /*
@@ -74,16 +68,21 @@ struct cache_entry
     des_block key;             /* conversation key */
     char *rname;               /* client's name */
     u_int window;              /* credential lifetime window */
-    struct timeval laststamp;  /* detect replays of creds */
+    struct rpc_timeval laststamp;      /* detect replays of creds */
     char *localcred;           /* generic local credential */
   };
-static struct cache_entry *authdes_cache /* [AUTHDES_CACHESZ] */ ;
-static short *authdes_lru /* [AUTHDES_CACHESZ] */ ;
+#ifdef _RPC_THREAD_SAFE_
+#define authdes_cache RPC_THREAD_VARIABLE(authdes_cache_s)
+#define authdes_lru RPC_THREAD_VARIABLE(authdes_lru_s)
+#else
+static struct cache_entry *authdes_cache;
+static int *authdes_lru;
+#endif
 
 static void cache_init (void) internal_function; /* initialize the cache */
-static short cache_spot (des_block *, char *, struct timeval *)
+static short cache_spot (des_block *, char *, struct rpc_timeval *)
      internal_function;                /* find an entry in the cache */
-static void cache_ref (short sid) internal_function;
+static void cache_ref (uint32_t sid) internal_function;
                                /* note that sid was ref'd */
 
 static void invalidate (char *cred) internal_function;
@@ -106,18 +105,18 @@ svcauthdes_stats;
 enum auth_stat
 _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
 {
-  register long *ixdr;
+  register uint32_t *ixdr;
   des_block cryptbuf[2];
   register struct authdes_cred *cred;
   struct authdes_verf verf;
   int status;
   register struct cache_entry *entry;
-  short sid = 0;
+  uint32_t sid = 0;
   des_block *sessionkey;
   des_block ivec;
   u_int window;
-  struct timeval timestamp;
-  u_long namelen;
+  struct rpc_timeval timestamp;
+  uint32_t namelen;
   struct area
     {
       struct authdes_cred area_cred;
@@ -127,6 +126,8 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
 
   if (authdes_cache == NULL)
     cache_init ();
+  if (authdes_cache == NULL) /* No free memory */
+    return AUTH_FAILED;
 
   area = (struct area *) rqst->rq_clntcred;
   cred = (struct authdes_cred *) &area->area_cred;
@@ -134,27 +135,30 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
   /*
    * Get the credential
    */
-  ixdr = (long *) msg->rm_call.cb_cred.oa_base;
+  if (msg->rm_call.cb_cred.oa_length <= 0 ||
+      msg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES)
+    return AUTH_BADCRED;
+
+  ixdr = (uint32_t *) msg->rm_call.cb_cred.oa_base;
   cred->adc_namekind = IXDR_GET_ENUM (ixdr, enum authdes_namekind);
   switch (cred->adc_namekind)
     {
     case ADN_FULLNAME:
-      namelen = IXDR_GET_U_LONG (ixdr);
+      namelen = IXDR_GET_U_INT32 (ixdr);
       if (namelen > MAXNETNAMELEN)
        {
          return AUTH_BADCRED;
        }
       cred->adc_fullname.name = area->area_netname;
-      bcopy ((char *) ixdr, cred->adc_fullname.name,
-            (u_int) namelen);
+      memcpy (cred->adc_fullname.name, (char *) ixdr, namelen);
       cred->adc_fullname.name[namelen] = 0;
       ixdr += (RNDUP (namelen) / BYTES_PER_XDR_UNIT);
-      cred->adc_fullname.key.key.high = (u_long) * ixdr++;
-      cred->adc_fullname.key.key.low = (u_long) * ixdr++;
-      cred->adc_fullname.window = (u_long) * ixdr++;
+      cred->adc_fullname.key.key.high = *ixdr++;
+      cred->adc_fullname.key.key.low = *ixdr++;
+      cred->adc_fullname.window = *ixdr++;
       break;
     case ADN_NICKNAME:
-      cred->adc_nickname = (u_long) * ixdr++;
+      cred->adc_nickname = *ixdr++;
       break;
     default:
       return AUTH_BADCRED;
@@ -163,11 +167,14 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
   /*
    * Get the verifier
    */
-  ixdr = (long *) msg->rm_call.cb_verf.oa_base;
-  verf.adv_xtimestamp.key.high = (u_long) * ixdr++;
-  verf.adv_xtimestamp.key.low = (u_long) * ixdr++;
-  verf.adv_int_u = (u_long) * ixdr++;
+  if (msg->rm_call.cb_verf.oa_length <= 0 ||
+      msg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES)
+    return AUTH_BADCRED;
 
+  ixdr = (uint32_t *) msg->rm_call.cb_verf.oa_base;
+  verf.adv_xtimestamp.key.high = *ixdr++;
+  verf.adv_xtimestamp.key.low = *ixdr++;
+  verf.adv_int_u = *ixdr++;
 
   /*
    * Get the conversation key
@@ -194,12 +201,18 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
     }
   else
     {                          /* ADN_NICKNAME */
-      sid = (short) cred->adc_nickname;
-      if (sid >= AUTHDES_CACHESZ)
+      if (cred->adc_nickname >= AUTHDES_CACHESZ)
        {
          debug ("bad nickname");
          return AUTH_BADCRED;  /* garbled credential */
        }
+      else
+       sid = cred->adc_nickname;
+
+      /* XXX This could be wrong, but else we have a
+        security problem */
+      if (authdes_cache[sid].rname == NULL)
+       return AUTH_BADCRED;
       sessionkey = &authdes_cache[sid].key;
     }
 
@@ -218,10 +231,9 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
                          (char *) &ivec);
     }
   else
-    {
-      status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf,
-                         sizeof (des_block), DES_DECRYPT | DES_HW);
-    }
+    status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf,
+                       sizeof (des_block), DES_DECRYPT | DES_HW);
+
   if (DES_FAILED (status))
     {
       debug ("decryption failure");
@@ -231,9 +243,9 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
   /*
    * XDR the decrypted timestamp
    */
-  ixdr = (long *) cryptbuf;
-  timestamp.tv_sec = IXDR_GET_LONG (ixdr);
-  timestamp.tv_usec = IXDR_GET_LONG (ixdr);
+  ixdr = (uint32_t *) cryptbuf;
+  timestamp.tv_sec = IXDR_GET_INT32 (ixdr);
+  timestamp.tv_usec = IXDR_GET_INT32 (ixdr);
 
   /*
    * Check for valid credentials and verifiers.
@@ -248,20 +260,23 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
 
     if (cred->adc_namekind == ADN_FULLNAME)
       {
-       window = IXDR_GET_U_LONG (ixdr);
-       winverf = IXDR_GET_U_LONG (ixdr);
+       short tmp_spot;
+
+       window = IXDR_GET_U_INT32 (ixdr);
+       winverf = IXDR_GET_U_INT32 (ixdr);
        if (winverf != window - 1)
          {
            debug ("window verifier mismatch");
            return AUTH_BADCRED;        /* garbled credential */
          }
-       sid = cache_spot (sessionkey, cred->adc_fullname.name,
-                         &timestamp);
-       if (sid < 0)
+       tmp_spot = cache_spot (sessionkey, cred->adc_fullname.name,
+                              &timestamp);
+       if (tmp_spot < 0 || tmp_spot > AUTHDES_CACHESZ)
          {
            debug ("replayed credential");
            return AUTH_REJECTEDCRED;           /* replay */
          }
+       sid = tmp_spot;
        nick = 0;
       }
     else
@@ -270,19 +285,18 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
        nick = 1;
       }
 
-    if ((u_long) timestamp.tv_usec >= USEC_PER_SEC)
+    if (timestamp.tv_usec >= USEC_PER_SEC)
       {
        debug ("invalid usecs");
        /* cached out (bad key), or garbled verifier */
        return nick ? AUTH_REJECTEDVERF : AUTH_BADVERF;
       }
-    if (nick && BEFORE (&timestamp,
-                       &authdes_cache[sid].laststamp))
+    if (nick && BEFORE (&timestamp, &authdes_cache[sid].laststamp))
       {
        debug ("timestamp before last seen");
-       return (AUTH_REJECTEDVERF);     /* replay */
+       return AUTH_REJECTEDVERF;       /* replay */
       }
-    gettimeofday (&current, (struct timezone *) NULL);
+    __gettimeofday (&current, (struct timezone *) NULL);
     current.tv_sec -= window;  /* allow for expiration */
     if (!BEFORE (&current, &timestamp))
       {
@@ -295,14 +309,14 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
   /*
    * Set up the reply verifier
    */
-  verf.adv_nickname = (u_long) sid;
+  verf.adv_nickname = sid;
 
   /*
    * xdr the timestamp before encrypting
    */
-  ixdr = (long *) cryptbuf;
-  IXDR_PUT_LONG (ixdr, timestamp.tv_sec - 1);
-  IXDR_PUT_LONG (ixdr, timestamp.tv_usec);
+  ixdr = (uint32_t *) cryptbuf;
+  IXDR_PUT_INT32 (ixdr, timestamp.tv_sec - 1);
+  IXDR_PUT_INT32 (ixdr, timestamp.tv_usec);
 
   /*
    * encrypt the timestamp
@@ -319,10 +333,10 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
   /*
    * Serialize the reply verifier, and update rqst
    */
-  ixdr = (long *) msg->rm_call.cb_verf.oa_base;
-  *ixdr++ = (long) verf.adv_xtimestamp.key.high;
-  *ixdr++ = (long) verf.adv_xtimestamp.key.low;
-  *ixdr++ = (long) verf.adv_int_u;
+  ixdr = (uint32_t *) msg->rm_call.cb_verf.oa_base;
+  *ixdr++ = verf.adv_xtimestamp.key.high;
+  *ixdr++ = verf.adv_xtimestamp.key.low;
+  *ixdr++ = verf.adv_int_u;
 
   rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
   rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
@@ -341,20 +355,17 @@ _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
       size_t full_len;
 
       cred->adc_fullname.window = window;
-      cred->adc_nickname = (u_long) sid;       /* save nickname */
+      cred->adc_nickname = sid;        /* save nickname */
       if (entry->rname != NULL)
-       {
-         mem_free (entry->rname, strlen (entry->rname) + 1);
-       }
+       mem_free (entry->rname, strlen (entry->rname) + 1);
       full_len = strlen (cred->adc_fullname.name) + 1;
       entry->rname = mem_alloc ((u_int) full_len);
       if (entry->rname != NULL)
-       {
-         memcpy (entry->rname, cred->adc_fullname.name, full_len);
-       }
+       memcpy (entry->rname, cred->adc_fullname.name, full_len);
       else
        {
          debug ("out of memory");
+         return AUTH_FAILED; /* out of memory is bad */
        }
       entry->key = *sessionkey;
       entry->window = window;
@@ -384,18 +395,16 @@ cache_init (void)
   register int i;
 
   authdes_cache = (struct cache_entry *)
-    mem_alloc (sizeof (struct cache_entry) * AUTHDES_CACHESZ);
-  bzero ((char *) authdes_cache,
-        sizeof (struct cache_entry) * AUTHDES_CACHESZ);
+    calloc (sizeof (struct cache_entry) * AUTHDES_CACHESZ, 1);
+  if (authdes_cache == NULL)
+    return;
 
-  authdes_lru = (short *) mem_alloc (sizeof (short) * AUTHDES_CACHESZ);
+  authdes_lru = (int *) mem_alloc (sizeof (int) * AUTHDES_CACHESZ);
   /*
    * Initialize the lru list
    */
-  for (i = 0; i < AUTHDES_CACHESZ; i++)
-    {
-      authdes_lru[i] = i;
-    }
+  for (i = 0; i < AUTHDES_CACHESZ; ++i)
+    authdes_lru[i] = i;
 }
 
 
@@ -405,7 +414,7 @@ cache_init (void)
 static short
 cache_victim (void)
 {
-  return (authdes_lru[AUTHDES_CACHESZ - 1]);
+  return authdes_lru[AUTHDES_CACHESZ - 1];
 }
 
 /*
@@ -413,15 +422,15 @@ cache_victim (void)
  */
 static void
 internal_function
-cache_ref (register short sid)
+cache_ref (register uint32_t sid)
 {
   register int i;
-  register short curr;
-  register short prev;
+  register int curr;
+  register int prev;
 
   prev = authdes_lru[0];
   authdes_lru[0] = sid;
-  for (i = 1; prev != sid; i++)
+  for (i = 1; prev != sid; ++i)
     {
       curr = authdes_lru[i];
       authdes_lru[i] = prev;
@@ -429,7 +438,6 @@ cache_ref (register short sid)
     }
 }
 
-
 /*
  * Find a spot in the cache for a credential containing
  * the items given.  Return -1 if a replay is detected, otherwise
@@ -437,34 +445,34 @@ cache_ref (register short sid)
  */
 static short
 internal_function
-cache_spot (register des_block * key, char *name, struct timeval *timestamp)
+cache_spot (register des_block *key, char *name,
+           struct rpc_timeval *timestamp)
 {
   register struct cache_entry *cp;
   register int i;
-  register u_long hi;
+  register uint32_t hi;
 
   hi = key->key.high;
-  for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++)
+  for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; ++i, ++cp)
     {
       if (cp->key.key.high == hi &&
          cp->key.key.low == key->key.low &&
          cp->rname != NULL &&
-         bcmp (cp->rname, name, strlen (name) + 1) == 0)
+         memcmp (cp->rname, name, strlen (name) + 1) == 0)
        {
          if (BEFORE (timestamp, &cp->laststamp))
            {
-             svcauthdes_stats.ncachereplays++;
+             ++svcauthdes_stats.ncachereplays;
              return -1;        /* replay */
            }
-         svcauthdes_stats.ncachehits++;
+         ++svcauthdes_stats.ncachehits;
          return i;             /* refresh */
        }
     }
-  svcauthdes_stats.ncachemisses++;
+  ++svcauthdes_stats.ncachemisses;
   return cache_victim ();      /* new credential */
 }
 
-
 /*
  * Local credential handling stuff.
  * NOTE: bsd unix dependent.
@@ -477,8 +485,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 */
 };
 
 /*
@@ -505,13 +514,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
@@ -520,17 +523,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;
-      for (i = i_grouplen - 1; i >= 0; i--)
-       {
-         cred->groups[i] = groups[i];  /* int to short */
-       }
+      cred->grouplen = i_grouplen;
+      for (i = i_grouplen - 1; i >= 0; --i)
+       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)
@@ -546,21 +575,22 @@ 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;
 }
+libc_hidden_nolink_sunrpc (authdes_getucred, GLIBC_2_1)
 
 static void
 internal_function
 invalidate (char *cred)
 {
   if (cred == NULL)
-    {
-      return;
-    }
+    return;
   ((struct bsdcred *) cred)->grouplen = INVALID;
 }