diff options
Diffstat (limited to 'sunrpc/svcauth_des.c')
-rw-r--r-- | sunrpc/svcauth_des.c | 597 |
1 files changed, 0 insertions, 597 deletions
diff --git a/sunrpc/svcauth_des.c b/sunrpc/svcauth_des.c deleted file mode 100644 index 8e6cb86e39..0000000000 --- a/sunrpc/svcauth_des.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (c) 2010, Oracle America, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * 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. - * - * 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. - * - * svcauth_des.c, server-side des authentication - * - * We insure for the service the following: - * (1) The timestamp microseconds do not exceed 1 million. - * (2) The timestamp plus the window is less than the current time. - * (3) The timestamp is not less than the one previously - * seen in the current session. - * - * It is up to the server to determine if the window size is - * too small . - * - */ - -#include <limits.h> -#include <string.h> -#include <stdint.h> -#include <sys/param.h> -#include <netinet/in.h> -#include <rpc/rpc.h> -#include <rpc/xdr.h> -#include <rpc/auth.h> -#include <rpc/auth_des.h> -#include <rpc/svc_auth.h> -#include <rpc/svc.h> -#include <rpc/des_crypt.h> -#include <shlib-compat.h> - -#define debug(msg) /*printf("svcauth_des: %s\n", msg) */ - -#define USEC_PER_SEC ((uint32_t) 1000000L) -#define BEFORE(t1, t2) timercmp(t1, t2, <) - -/* - * LRU cache of conversation keys and some other useful items. - */ -#define AUTHDES_CACHESZ 64 -struct cache_entry - { - des_block key; /* conversation key */ - char *rname; /* client's name */ - u_int window; /* credential lifetime window */ - struct rpc_timeval laststamp; /* detect replays of creds */ - char *localcred; /* generic local credential */ - }; -#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 rpc_timeval *) - internal_function; /* find an entry in the cache */ -static void cache_ref (uint32_t sid) internal_function; - /* note that sid was ref'd */ - -static void invalidate (char *cred) internal_function; - /* invalidate entry in cache */ - -/* - * cache statistics - */ -struct - { - u_long ncachehits; /* times cache hit, and is not replay */ - u_long ncachereplays; /* times cache hit, and is replay */ - u_long ncachemisses; /* times cache missed */ - } -svcauthdes_stats; - -/* - * Service side authenticator for AUTH_DES - */ -enum auth_stat -_svcauth_des (register struct svc_req *rqst, register struct rpc_msg *msg) -{ - register uint32_t *ixdr; - des_block cryptbuf[2]; - register struct authdes_cred *cred; - struct authdes_verf verf; - int status; - register struct cache_entry *entry; - uint32_t sid = 0; - des_block *sessionkey; - des_block ivec; - u_int window; - struct rpc_timeval timestamp; - uint32_t namelen; - struct area - { - struct authdes_cred area_cred; - char area_netname[MAXNETNAMELEN + 1]; - } - *area; - - 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; - - /* - * Get the credential - */ - 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_INT32 (ixdr); - if (namelen > MAXNETNAMELEN) - { - return AUTH_BADCRED; - } - cred->adc_fullname.name = area->area_netname; - 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 = *ixdr++; - cred->adc_fullname.key.key.low = *ixdr++; - cred->adc_fullname.window = *ixdr++; - break; - case ADN_NICKNAME: - cred->adc_nickname = *ixdr++; - break; - default: - return AUTH_BADCRED; - } - - /* - * Get the verifier - */ - 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 - */ - if (cred->adc_namekind == ADN_FULLNAME) - { - netobj pkey; - char pkey_data[1024]; - - sessionkey = &cred->adc_fullname.key; - if (!getpublickey (cred->adc_fullname.name, pkey_data)) - { - debug("getpublickey"); - return AUTH_BADCRED; - } - pkey.n_bytes = pkey_data; - pkey.n_len = strlen (pkey_data) + 1; - if (key_decryptsession_pk (cred->adc_fullname.name, &pkey, - sessionkey) < 0) - { - debug ("decryptsessionkey"); - return AUTH_BADCRED; /* key not found */ - } - } - else - { /* ADN_NICKNAME */ - 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; - } - - - /* - * Decrypt the timestamp - */ - cryptbuf[0] = verf.adv_xtimestamp; - if (cred->adc_namekind == ADN_FULLNAME) - { - cryptbuf[1].key.high = cred->adc_fullname.window; - cryptbuf[1].key.low = verf.adv_winverf; - ivec.key.high = ivec.key.low = 0; - status = cbc_crypt ((char *) sessionkey, (char *) cryptbuf, - 2 * sizeof (des_block), DES_DECRYPT | DES_HW, - (char *) &ivec); - } - else - status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf, - sizeof (des_block), DES_DECRYPT | DES_HW); - - if (DES_FAILED (status)) - { - debug ("decryption failure"); - return AUTH_FAILED; /* system error */ - } - - /* - * XDR the decrypted timestamp - */ - ixdr = (uint32_t *) cryptbuf; - timestamp.tv_sec = IXDR_GET_INT32 (ixdr); - timestamp.tv_usec = IXDR_GET_INT32 (ixdr); - - /* - * Check for valid credentials and verifiers. - * They could be invalid because the key was flushed - * out of the cache, and so a new session should begin. - * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case. - */ - { - struct timeval current; - int nick; - u_int winverf; - - if (cred->adc_namekind == ADN_FULLNAME) - { - 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 */ - } - tmp_spot = cache_spot (sessionkey, cred->adc_fullname.name, - ×tamp); - if (tmp_spot < 0 || tmp_spot > AUTHDES_CACHESZ) - { - debug ("replayed credential"); - return AUTH_REJECTEDCRED; /* replay */ - } - sid = tmp_spot; - nick = 0; - } - else - { /* ADN_NICKNAME */ - window = authdes_cache[sid].window; - nick = 1; - } - - 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 (×tamp, &authdes_cache[sid].laststamp)) - { - debug ("timestamp before last seen"); - return AUTH_REJECTEDVERF; /* replay */ - } - __gettimeofday (¤t, (struct timezone *) NULL); - current.tv_sec -= window; /* allow for expiration */ - if (!BEFORE (¤t, ×tamp)) - { - debug ("timestamp expired"); - /* replay, or garbled credential */ - return nick ? AUTH_REJECTEDVERF : AUTH_BADCRED; - } - } - - /* - * Set up the reply verifier - */ - verf.adv_nickname = sid; - - /* - * xdr the timestamp before encrypting - */ - ixdr = (uint32_t *) cryptbuf; - IXDR_PUT_INT32 (ixdr, timestamp.tv_sec - 1); - IXDR_PUT_INT32 (ixdr, timestamp.tv_usec); - - /* - * encrypt the timestamp - */ - status = ecb_crypt ((char *) sessionkey, (char *) cryptbuf, - sizeof (des_block), DES_ENCRYPT | DES_HW); - if (DES_FAILED (status)) - { - debug ("encryption failure"); - return AUTH_FAILED; /* system error */ - } - verf.adv_xtimestamp = cryptbuf[0]; - - /* - * Serialize the reply verifier, and update rqst - */ - 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; - rqst->rq_xprt->xp_verf.oa_length = - (char *) ixdr - msg->rm_call.cb_verf.oa_base; - - /* - * We succeeded, commit the data to the cache now and - * finish cooking the credential. - */ - entry = &authdes_cache[sid]; - entry->laststamp = timestamp; - cache_ref (sid); - if (cred->adc_namekind == ADN_FULLNAME) - { - size_t full_len; - - cred->adc_fullname.window = window; - cred->adc_nickname = sid; /* save nickname */ - if (entry->rname != NULL) - 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); - else - { - debug ("out of memory"); - return AUTH_FAILED; /* out of memory is bad */ - } - entry->key = *sessionkey; - entry->window = window; - invalidate (entry->localcred); /* mark any cached cred invalid */ - } - else - { /* ADN_NICKNAME */ - /* - * nicknames are cooked into fullnames - */ - cred->adc_namekind = ADN_FULLNAME; - cred->adc_fullname.name = entry->rname; - cred->adc_fullname.key = entry->key; - cred->adc_fullname.window = entry->window; - } - return AUTH_OK; /* we made it! */ -} - - -/* - * Initialize the cache - */ -static void -internal_function -cache_init (void) -{ - register int i; - - authdes_cache = (struct cache_entry *) - calloc (sizeof (struct cache_entry) * AUTHDES_CACHESZ, 1); - if (authdes_cache == NULL) - return; - - authdes_lru = (int *) mem_alloc (sizeof (int) * AUTHDES_CACHESZ); - /* - * Initialize the lru list - */ - for (i = 0; i < AUTHDES_CACHESZ; ++i) - authdes_lru[i] = i; -} - - -/* - * Find the lru victim - */ -static short -cache_victim (void) -{ - return authdes_lru[AUTHDES_CACHESZ - 1]; -} - -/* - * Note that sid was referenced - */ -static void -internal_function -cache_ref (register uint32_t sid) -{ - register int i; - register int curr; - register int prev; - - prev = authdes_lru[0]; - authdes_lru[0] = sid; - for (i = 1; prev != sid; ++i) - { - curr = authdes_lru[i]; - authdes_lru[i] = prev; - prev = curr; - } -} - -/* - * Find a spot in the cache for a credential containing - * the items given. Return -1 if a replay is detected, otherwise - * return the spot in the cache. - */ -static short -internal_function -cache_spot (register des_block *key, char *name, - struct rpc_timeval *timestamp) -{ - register struct cache_entry *cp; - register int i; - register uint32_t hi; - - hi = key->key.high; - 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 && - memcmp (cp->rname, name, strlen (name) + 1) == 0) - { - if (BEFORE (timestamp, &cp->laststamp)) - { - ++svcauthdes_stats.ncachereplays; - return -1; /* replay */ - } - ++svcauthdes_stats.ncachehits; - return i; /* refresh */ - } - } - ++svcauthdes_stats.ncachemisses; - return cache_victim (); /* new credential */ -} - -/* - * Local credential handling stuff. - * NOTE: bsd unix dependent. - * Other operating systems should put something else here. - */ -#define UNKNOWN -2 /* grouplen, if cached cred is unknown user */ -#define INVALID -1 /* grouplen, if cache entry is invalid */ - -struct bsdcred -{ - uid_t uid; /* cached uid */ - gid_t gid; /* cached gid */ - int grouplen; /* length of cached groups */ - int grouplen_max; /* length of allocated cached groups */ - gid_t groups[0]; /* cached groups */ -}; - -/* - * Map a des credential into a unix cred. - * We cache the credential here so the application does - * not have to make an rpc call every time to interpret - * the credential. - */ -int -authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid, - short *grouplen, gid_t * groups) -{ - unsigned sid; - register int i; - uid_t i_uid; - gid_t i_gid; - int i_grouplen; - struct bsdcred *cred; - - sid = adc->adc_nickname; - if (sid >= AUTHDES_CACHESZ) - { - debug ("invalid nickname"); - return 0; - } - cred = (struct bsdcred *) authdes_cache[sid].localcred; - if (cred == NULL || cred->grouplen == INVALID) - { - /* - * not in cache: lookup - */ - if (!netname2user (adc->adc_fullname.name, &i_uid, &i_gid, - &i_grouplen, groups)) - { - debug ("unknown netname"); - 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; - 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) - { - /* - * Already lookup up, but no match found - */ - return 0; - } - - /* - * cached credentials - */ - *uid = cred->uid; - *gid = cred->gid; - - /* 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; - ((struct bsdcred *) cred)->grouplen = INVALID; -} |