(root)/
glibc-2.38/
sunrpc/
svcauth_des.c
       1  /*
       2   * Copyright (c) 2010, Oracle America, Inc.
       3   *
       4   * Redistribution and use in source and binary forms, with or without
       5   * modification, are permitted provided that the following conditions are
       6   * met:
       7   *
       8   *     * Redistributions of source code must retain the above copyright
       9   *       notice, this list of conditions and the following disclaimer.
      10   *     * Redistributions in binary form must reproduce the above
      11   *       copyright notice, this list of conditions and the following
      12   *       disclaimer in the documentation and/or other materials
      13   *       provided with the distribution.
      14   *     * Neither the name of the "Oracle America, Inc." nor the names of its
      15   *       contributors may be used to endorse or promote products derived
      16   *       from this software without specific prior written permission.
      17   *
      18   *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19   *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20   *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      21   *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      22   *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      23   *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      24   *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
      25   *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      26   *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      27   *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      28   *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30   *
      31   * svcauth_des.c, server-side des authentication
      32   *
      33   * We insure for the service the following:
      34   * (1) The timestamp microseconds do not exceed 1 million.
      35   * (2) The timestamp plus the window is less than the current time.
      36   * (3) The timestamp is not less than the one previously
      37   *     seen in the current session.
      38   *
      39   * It is up to the server to determine if the window size is
      40   * too small .
      41   *
      42   */
      43  
      44  #include <limits.h>
      45  #include <string.h>
      46  #include <stdint.h>
      47  #include <time.h>
      48  #include <sys/param.h>
      49  #include <netinet/in.h>
      50  #include <rpc/rpc.h>
      51  #include <rpc/xdr.h>
      52  #include <rpc/auth.h>
      53  #include <rpc/auth_des.h>
      54  #include <rpc/svc_auth.h>
      55  #include <rpc/svc.h>
      56  #include <rpc/des_crypt.h>
      57  #include <shlib-compat.h>
      58  
      59  #define debug(msg)		/*printf("svcauth_des: %s\n", msg) */
      60  
      61  #define BEFORE(t1, t2) timercmp(t1, t2, <)
      62  
      63  /*
      64   * LRU cache of conversation keys and some other useful items.
      65   */
      66  #define AUTHDES_CACHESZ 64
      67  struct cache_entry
      68    {
      69      des_block key;		/* conversation key */
      70      char *rname;		/* client's name */
      71      u_int window;		/* credential lifetime window */
      72      struct rpc_timeval laststamp;	/* detect replays of creds */
      73      char *localcred;		/* generic local credential */
      74    };
      75  #define authdes_cache RPC_THREAD_VARIABLE(authdes_cache_s)
      76  #define authdes_lru RPC_THREAD_VARIABLE(authdes_lru_s)
      77  
      78  static void cache_init (void); /* initialize the cache */
      79  static short cache_spot (des_block *, char *, struct rpc_timeval *);
      80    /* find an entry in the cache */
      81  static void cache_ref (uint32_t sid); /* note that sid was ref'd */
      82  
      83  static void invalidate (char *cred); /* invalidate entry in cache */
      84  
      85  /* Cache statistics.  Accidental historic export without a matching
      86     declaration in any header file.  */
      87  #ifndef SHARED
      88  static
      89  #endif
      90  struct
      91    {
      92      u_long ncachehits;		/* times cache hit, and is not replay */
      93      u_long ncachereplays;	/* times cache hit, and is replay */
      94      u_long ncachemisses;	/* times cache missed */
      95    }
      96  svcauthdes_stats;
      97  #ifdef SHARED
      98  compat_symbol (libc, svcauthdes_stats, svcauthdes_stats, GLIBC_2_0);
      99  #endif
     100  
     101  /*
     102   * Service side authenticator for AUTH_DES
     103   */
     104  enum auth_stat
     105  _svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg)
     106  {
     107    register uint32_t *ixdr;
     108    des_block cryptbuf[2];
     109    register struct authdes_cred *cred;
     110    struct authdes_verf verf;
     111    int status;
     112    register struct cache_entry *entry;
     113    uint32_t sid = 0;
     114    des_block *sessionkey;
     115    des_block ivec;
     116    u_int window;
     117    struct rpc_timeval timestamp;
     118    uint32_t namelen;
     119    struct area
     120      {
     121        struct authdes_cred area_cred;
     122        char area_netname[MAXNETNAMELEN + 1];
     123      }
     124     *area;
     125  
     126    if (authdes_cache == NULL)
     127      cache_init ();
     128    if (authdes_cache == NULL) /* No free memory */
     129      return AUTH_FAILED;
     130  
     131    area = (struct area *) rqst->rq_clntcred;
     132    cred = (struct authdes_cred *) &area->area_cred;
     133  
     134    /*
     135     * Get the credential
     136     */
     137    if (msg->rm_call.cb_cred.oa_length <= 0 ||
     138        msg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES)
     139      return AUTH_BADCRED;
     140  
     141    ixdr = (uint32_t *) msg->rm_call.cb_cred.oa_base;
     142    cred->adc_namekind = IXDR_GET_ENUM (ixdr, enum authdes_namekind);
     143    switch (cred->adc_namekind)
     144      {
     145      case ADN_FULLNAME:
     146        namelen = IXDR_GET_U_INT32 (ixdr);
     147        if (namelen > MAXNETNAMELEN)
     148  	{
     149  	  return AUTH_BADCRED;
     150  	}
     151        cred->adc_fullname.name = area->area_netname;
     152        memcpy (cred->adc_fullname.name, (char *) ixdr, namelen);
     153        cred->adc_fullname.name[namelen] = 0;
     154        ixdr += (RNDUP (namelen) / BYTES_PER_XDR_UNIT);
     155        cred->adc_fullname.key.key.high = *ixdr++;
     156        cred->adc_fullname.key.key.low = *ixdr++;
     157        cred->adc_fullname.window = *ixdr++;
     158        break;
     159      case ADN_NICKNAME:
     160        cred->adc_nickname = *ixdr++;
     161        break;
     162      default:
     163        return AUTH_BADCRED;
     164      }
     165  
     166    /*
     167     * Get the verifier
     168     */
     169    if (msg->rm_call.cb_verf.oa_length <= 0 ||
     170        msg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES)
     171      return AUTH_BADCRED;
     172  
     173    ixdr = (uint32_t *) msg->rm_call.cb_verf.oa_base;
     174    verf.adv_xtimestamp.key.high = *ixdr++;
     175    verf.adv_xtimestamp.key.low = *ixdr++;
     176    verf.adv_int_u = *ixdr++;
     177  
     178    /*
     179     * Get the conversation key
     180     */
     181    if (cred->adc_namekind == ADN_FULLNAME)
     182      {
     183        netobj pkey;
     184        char pkey_data[1024];
     185  
     186        sessionkey = &cred->adc_fullname.key;
     187        if (!getpublickey (cred->adc_fullname.name, pkey_data))
     188  	{
     189  	  debug("getpublickey");
     190  	  return AUTH_BADCRED;
     191  	}
     192        pkey.n_bytes = pkey_data;
     193        pkey.n_len = strlen (pkey_data) + 1;
     194        if (key_decryptsession_pk (cred->adc_fullname.name, &pkey,
     195  				 sessionkey) < 0)
     196  	{
     197  	  debug ("decryptsessionkey");
     198  	  return AUTH_BADCRED;	/* key not found */
     199  	}
     200      }
     201    else
     202      {				/* ADN_NICKNAME */
     203        if (cred->adc_nickname >= AUTHDES_CACHESZ)
     204  	{
     205  	  debug ("bad nickname");
     206  	  return AUTH_BADCRED;	/* garbled credential */
     207  	}
     208        else
     209  	sid = cred->adc_nickname;
     210  
     211        /* XXX This could be wrong, but else we have a
     212  	 security problem */
     213        if (authdes_cache[sid].rname == NULL)
     214  	return AUTH_BADCRED;
     215        sessionkey = &authdes_cache[sid].key;
     216      }
     217  
     218  
     219    /*
     220     * Decrypt the timestamp
     221     */
     222    cryptbuf[0] = verf.adv_xtimestamp;
     223    if (cred->adc_namekind == ADN_FULLNAME)
     224      {
     225        cryptbuf[1].key.high = cred->adc_fullname.window;
     226        cryptbuf[1].key.low = verf.adv_winverf;
     227        ivec.key.high = ivec.key.low = 0;
     228        status = cbc_crypt ((char *) sessionkey, (char *) cryptbuf,
     229  			  2 * sizeof (des_block), DES_DECRYPT | DES_HW,
     230  			  (char *) &ivec);
     231      }
     232    else
     233      status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf,
     234  			sizeof (des_block), DES_DECRYPT | DES_HW);
     235  
     236    if (DES_FAILED (status))
     237      {
     238        debug ("decryption failure");
     239        return AUTH_FAILED;	/* system error */
     240      }
     241  
     242    /*
     243     * XDR the decrypted timestamp
     244     */
     245    ixdr = (uint32_t *) cryptbuf;
     246    timestamp.tv_sec = IXDR_GET_INT32 (ixdr);
     247    timestamp.tv_usec = IXDR_GET_INT32 (ixdr);
     248  
     249    /*
     250     * Check for valid credentials and verifiers.
     251     * They could be invalid because the key was flushed
     252     * out of the cache, and so a new session should begin.
     253     * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
     254     */
     255    {
     256      struct timeval current;
     257      int nick;
     258      u_int winverf;
     259  
     260      if (cred->adc_namekind == ADN_FULLNAME)
     261        {
     262  	short tmp_spot;
     263  
     264  	window = IXDR_GET_U_INT32 (ixdr);
     265  	winverf = IXDR_GET_U_INT32 (ixdr);
     266  	if (winverf != window - 1)
     267  	  {
     268  	    debug ("window verifier mismatch");
     269  	    return AUTH_BADCRED;	/* garbled credential */
     270  	  }
     271  	tmp_spot = cache_spot (sessionkey, cred->adc_fullname.name,
     272  			       &timestamp);
     273  	if (tmp_spot < 0 || tmp_spot > AUTHDES_CACHESZ)
     274  	  {
     275  	    debug ("replayed credential");
     276  	    return AUTH_REJECTEDCRED;		/* replay */
     277  	  }
     278  	sid = tmp_spot;
     279  	nick = 0;
     280        }
     281      else
     282        {				/* ADN_NICKNAME */
     283  	window = authdes_cache[sid].window;
     284  	nick = 1;
     285        }
     286  
     287      if (timestamp.tv_usec >= USEC_PER_SEC)
     288        {
     289  	debug ("invalid usecs");
     290  	/* cached out (bad key), or garbled verifier */
     291  	return nick ? AUTH_REJECTEDVERF : AUTH_BADVERF;
     292        }
     293      if (nick && BEFORE (&timestamp, &authdes_cache[sid].laststamp))
     294        {
     295  	debug ("timestamp before last seen");
     296  	return AUTH_REJECTEDVERF;	/* replay */
     297        }
     298      {
     299        struct timespec now;
     300        __clock_gettime (CLOCK_REALTIME, &now);
     301        TIMESPEC_TO_TIMEVAL (&current, &now);
     302      }
     303      current.tv_sec -= window;	/* allow for expiration */
     304      if (!BEFORE (&current, &timestamp))
     305        {
     306  	debug ("timestamp expired");
     307  	/* replay, or garbled credential */
     308  	return nick ? AUTH_REJECTEDVERF : AUTH_BADCRED;
     309        }
     310    }
     311  
     312    /*
     313     * Set up the reply verifier
     314     */
     315    verf.adv_nickname = sid;
     316  
     317    /*
     318     * xdr the timestamp before encrypting
     319     */
     320    ixdr = (uint32_t *) cryptbuf;
     321    IXDR_PUT_INT32 (ixdr, timestamp.tv_sec - 1);
     322    IXDR_PUT_INT32 (ixdr, timestamp.tv_usec);
     323  
     324    /*
     325     * encrypt the timestamp
     326     */
     327    status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf,
     328  		      sizeof (des_block), DES_ENCRYPT | DES_HW);
     329    if (DES_FAILED (status))
     330      {
     331        debug ("encryption failure");
     332        return AUTH_FAILED;	/* system error */
     333      }
     334    verf.adv_xtimestamp = cryptbuf[0];
     335  
     336    /*
     337     * Serialize the reply verifier, and update rqst
     338     */
     339    ixdr = (uint32_t *) msg->rm_call.cb_verf.oa_base;
     340    *ixdr++ = verf.adv_xtimestamp.key.high;
     341    *ixdr++ = verf.adv_xtimestamp.key.low;
     342    *ixdr++ = verf.adv_int_u;
     343  
     344    rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
     345    rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
     346    rqst->rq_xprt->xp_verf.oa_length =
     347      (char *) ixdr - msg->rm_call.cb_verf.oa_base;
     348  
     349    /*
     350     * We succeeded, commit the data to the cache now and
     351     * finish cooking the credential.
     352     */
     353    entry = &authdes_cache[sid];
     354    entry->laststamp = timestamp;
     355    cache_ref (sid);
     356    if (cred->adc_namekind == ADN_FULLNAME)
     357      {
     358        size_t full_len;
     359  
     360        cred->adc_fullname.window = window;
     361        cred->adc_nickname = sid;	/* save nickname */
     362        if (entry->rname != NULL)
     363  	mem_free (entry->rname, strlen (entry->rname) + 1);
     364        full_len = strlen (cred->adc_fullname.name) + 1;
     365        entry->rname = mem_alloc ((u_int) full_len);
     366        if (entry->rname != NULL)
     367  	memcpy (entry->rname, cred->adc_fullname.name, full_len);
     368        else
     369  	{
     370  	  debug ("out of memory");
     371  	  return AUTH_FAILED; /* out of memory is bad */
     372  	}
     373        entry->key = *sessionkey;
     374        entry->window = window;
     375        invalidate (entry->localcred);	/* mark any cached cred invalid */
     376      }
     377    else
     378      {				/* ADN_NICKNAME */
     379        /*
     380         * nicknames are cooked into fullnames
     381         */
     382        cred->adc_namekind = ADN_FULLNAME;
     383        cred->adc_fullname.name = entry->rname;
     384        cred->adc_fullname.key = entry->key;
     385        cred->adc_fullname.window = entry->window;
     386      }
     387    return AUTH_OK;		/* we made it! */
     388  }
     389  
     390  
     391  /*
     392   * Initialize the cache
     393   */
     394  static void
     395  cache_init (void)
     396  {
     397    register int i;
     398  
     399    authdes_cache = (struct cache_entry *)
     400      calloc (sizeof (struct cache_entry) * AUTHDES_CACHESZ, 1);
     401    if (authdes_cache == NULL)
     402      return;
     403  
     404    authdes_lru = (int *) mem_alloc (sizeof (int) * AUTHDES_CACHESZ);
     405    /*
     406     * Initialize the lru list
     407     */
     408    for (i = 0; i < AUTHDES_CACHESZ; ++i)
     409      authdes_lru[i] = i;
     410  }
     411  
     412  
     413  /*
     414   * Find the lru victim
     415   */
     416  static short
     417  cache_victim (void)
     418  {
     419    return authdes_lru[AUTHDES_CACHESZ - 1];
     420  }
     421  
     422  /*
     423   * Note that sid was referenced
     424   */
     425  static void
     426  cache_ref (register uint32_t sid)
     427  {
     428    register int i;
     429    register int curr;
     430    register int prev;
     431  
     432    prev = authdes_lru[0];
     433    authdes_lru[0] = sid;
     434    for (i = 1; prev != sid; ++i)
     435      {
     436        curr = authdes_lru[i];
     437        authdes_lru[i] = prev;
     438        prev = curr;
     439      }
     440  }
     441  
     442  /*
     443   * Find a spot in the cache for a credential containing
     444   * the items given.  Return -1 if a replay is detected, otherwise
     445   * return the spot in the cache.
     446   */
     447  static short
     448  cache_spot (register des_block *key, char *name,
     449  	    struct rpc_timeval *timestamp)
     450  {
     451    register struct cache_entry *cp;
     452    register int i;
     453    register uint32_t hi;
     454  
     455    hi = key->key.high;
     456    for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; ++i, ++cp)
     457      {
     458        if (cp->key.key.high == hi &&
     459  	  cp->key.key.low == key->key.low &&
     460  	  cp->rname != NULL &&
     461  	  memcmp (cp->rname, name, strlen (name) + 1) == 0)
     462  	{
     463  	  if (BEFORE (timestamp, &cp->laststamp))
     464  	    {
     465  	      ++svcauthdes_stats.ncachereplays;
     466  	      return -1;	/* replay */
     467  	    }
     468  	  ++svcauthdes_stats.ncachehits;
     469  	  return i;		/* refresh */
     470  	}
     471      }
     472    ++svcauthdes_stats.ncachemisses;
     473    return cache_victim ();	/* new credential */
     474  }
     475  
     476  /*
     477   * Local credential handling stuff.
     478   * NOTE: bsd unix dependent.
     479   * Other operating systems should put something else here.
     480   */
     481  #define UNKNOWN 	-2	/* grouplen, if cached cred is unknown user */
     482  #define INVALID		-1	/* grouplen, if cache entry is invalid */
     483  
     484  struct bsdcred
     485  {
     486    uid_t uid;			/* cached uid */
     487    gid_t gid;			/* cached gid */
     488    int grouplen;			/* length of cached groups */
     489    int grouplen_max;		/* length of allocated cached groups */
     490    gid_t groups[0];		/* cached groups */
     491  };
     492  
     493  /*
     494   * Map a des credential into a unix cred.
     495   * We cache the credential here so the application does
     496   * not have to make an rpc call every time to interpret
     497   * the credential.
     498   */
     499  int
     500  authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid,
     501  		  short *grouplen, gid_t * groups)
     502  {
     503    unsigned sid;
     504    register int i;
     505    uid_t i_uid;
     506    gid_t i_gid;
     507    int i_grouplen;
     508    struct bsdcred *cred;
     509  
     510    sid = adc->adc_nickname;
     511    if (sid >= AUTHDES_CACHESZ)
     512      {
     513        debug ("invalid nickname");
     514        return 0;
     515      }
     516    cred = (struct bsdcred *) authdes_cache[sid].localcred;
     517    if (cred == NULL || cred->grouplen == INVALID)
     518      {
     519        /*
     520         * not in cache: lookup
     521         */
     522        if (!netname2user (adc->adc_fullname.name, &i_uid, &i_gid,
     523  			 &i_grouplen, groups))
     524  	{
     525  	  debug ("unknown netname");
     526  	  if (cred != NULL)
     527  	    cred->grouplen = UNKNOWN;	/* mark as lookup up, but not found */
     528  	  return 0;
     529  	}
     530  
     531        if (cred != NULL && cred->grouplen_max < i_grouplen)
     532  	{
     533  	  /* We already have an allocated data structure.  But it is
     534  	     too small.  */
     535  	  free (cred);
     536  	  authdes_cache[sid].localcred = NULL;
     537  	  cred = NULL;
     538  	}
     539  
     540        if (cred == NULL)
     541  	{
     542  	  /* We should allocate room for at least NGROUPS groups.  */
     543  	  int ngroups_max = MAX (i_grouplen, NGROUPS);
     544  
     545  	  cred = (struct bsdcred *) mem_alloc (sizeof (struct bsdcred)
     546  					       + ngroups_max * sizeof (gid_t));
     547  	  if (cred == NULL)
     548  	    return 0;
     549  
     550  	  authdes_cache[sid].localcred = (char *) cred;
     551  	  cred->grouplen = INVALID;
     552  	  cred->grouplen_max = ngroups_max;
     553  	}
     554  
     555        debug ("missed ucred cache");
     556        *uid = cred->uid = i_uid;
     557        *gid = cred->gid = i_gid;
     558        cred->grouplen = i_grouplen;
     559        for (i = i_grouplen - 1; i >= 0; --i)
     560  	cred->groups[i] = groups[i];
     561        /* Make sure no too large values are reported.  */
     562        *grouplen = MIN (SHRT_MAX, i_grouplen);
     563        return 1;
     564      }
     565    else if (cred->grouplen == UNKNOWN)
     566      {
     567        /*
     568         * Already lookup up, but no match found
     569         */
     570        return 0;
     571      }
     572  
     573    /*
     574     * cached credentials
     575     */
     576    *uid = cred->uid;
     577    *gid = cred->gid;
     578  
     579    /* Another stupidity in the interface: *grouplen is of type short.
     580       So we might have to cut the information passed up short.  */
     581    int grouplen_copy = MIN (SHRT_MAX, cred->grouplen);
     582    *grouplen = grouplen_copy;
     583    for (i = grouplen_copy - 1; i >= 0; --i)
     584      groups[i] = cred->groups[i];
     585    return 1;
     586  }
     587  libc_hidden_nolink_sunrpc (authdes_getucred, GLIBC_2_1)
     588  
     589  static void
     590  invalidate (char *cred)
     591  {
     592    if (cred == NULL)
     593      return;
     594    ((struct bsdcred *) cred)->grouplen = INVALID;
     595  }