126219Swpaul
226219Swpaul/*
326219Swpaul * Copyright (c) 1988 by Sun Microsystems, Inc.
426219Swpaul */
526219Swpaul
626219Swpaul/*
726219Swpaul * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
826219Swpaul * unrestricted use provided that this legend is included on all tape
926219Swpaul * media and as a part of the software program in whole or part.  Users
1026219Swpaul * may copy or modify Sun RPC without charge, but are not authorized
1126219Swpaul * to license or distribute it to anyone else except as part of a product or
1226219Swpaul * program developed by the user.
1326219Swpaul *
1426219Swpaul * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
1526219Swpaul * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
1626219Swpaul * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
1726219Swpaul *
1826219Swpaul * Sun RPC is provided with no support and without any obligation on the
1926219Swpaul * part of Sun Microsystems, Inc. to assist in its use, correction,
2026219Swpaul * modification or enhancement.
2126219Swpaul *
2226219Swpaul * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
2326219Swpaul * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
2426219Swpaul * OR ANY PART THEREOF.
2526219Swpaul *
2626219Swpaul * In no event will Sun Microsystems, Inc. be liable for any lost revenue
2726219Swpaul * or profits or other special, indirect and consequential damages, even if
2826219Swpaul * Sun has been advised of the possibility of such damages.
2926219Swpaul *
3026219Swpaul * Sun Microsystems, Inc.
3126219Swpaul * 2550 Garcia Avenue
3226219Swpaul * Mountain View, California  94043
3326219Swpaul */
3426219Swpaul
3526219Swpaul/*
3626219Swpaul * svcauth_des.c, server-side des authentication
3726219Swpaul *
3826219Swpaul * We insure for the service the following:
3926219Swpaul * (1) The timestamp microseconds do not exceed 1 million.
4026219Swpaul * (2) The timestamp plus the window is less than the current time.
4126219Swpaul * (3) The timestamp is not less than the one previously
4226219Swpaul *     seen in the current session.
4326219Swpaul *
4426219Swpaul * It is up to the server to determine if the window size is
4526219Swpaul * too small .
4626219Swpaul *
4726219Swpaul */
4826219Swpaul
4975094Siedowse#include "namespace.h"
5074462Salfred#include "reentrant.h"
5126219Swpaul#include <string.h>
5226219Swpaul#include <stdlib.h>
5371579Sdeischen#include <stdio.h>
5426219Swpaul#include <unistd.h>
5526219Swpaul#include <rpc/des_crypt.h>
5626219Swpaul#include <sys/param.h>
5726219Swpaul#include <netinet/in.h>
5826219Swpaul#include <rpc/types.h>
5926219Swpaul#include <rpc/xdr.h>
6026219Swpaul#include <rpc/auth.h>
6126219Swpaul#include <rpc/auth_des.h>
6226219Swpaul#include <rpc/svc.h>
6326219Swpaul#include <rpc/rpc_msg.h>
6426219Swpaul#include <rpc/svc_auth.h>
6574462Salfred#include "libc_private.h"
6626219Swpaul
6726219Swpaul#if defined(LIBC_SCCS) && !defined(lint)
6892990Sobrienstatic char sccsid[] = 	"@(#)svcauth_des.c	2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI";
6926219Swpaul#endif
7092990Sobrien#include <sys/cdefs.h>
7192990Sobrien__FBSDID("$FreeBSD$");
7226219Swpaul
7390271Salfredextern int key_decryptsession_pk(const char *, netobj *, des_block *);
7490271Salfred
7526219Swpaul#define debug(msg)	 printf("svcauth_des: %s\n", msg)
7626219Swpaul
7726219Swpaul#define USEC_PER_SEC ((u_long) 1000000L)
7826219Swpaul#define BEFORE(t1, t2) timercmp(t1, t2, <)
7926219Swpaul
8026219Swpaul/*
8126219Swpaul * LRU cache of conversation keys and some other useful items.
8226219Swpaul */
8326219Swpaul#define AUTHDES_CACHESZ 64
8426219Swpaulstruct cache_entry {
8526219Swpaul	des_block key;		/* conversation key */
8626219Swpaul	char *rname;		/* client's name */
8726219Swpaul	u_int window;		/* credential lifetime window */
8826219Swpaul	struct timeval laststamp;	/* detect replays of creds */
8926219Swpaul	char *localcred;	/* generic local credential */
9026219Swpaul};
9126219Swpaulstatic struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
9226219Swpaulstatic short *authdes_lru/* [AUTHDES_CACHESZ] */;
9326219Swpaul
9426219Swpaulstatic void cache_init();	/* initialize the cache */
9526219Swpaulstatic short cache_spot();	/* find an entry in the cache */
9626219Swpaulstatic void cache_ref(/*short sid*/);	/* note that sid was ref'd */
9726219Swpaul
9826219Swpaulstatic void invalidate();	/* invalidate entry in cache */
9926219Swpaul
10026219Swpaul/*
10126219Swpaul * cache statistics
10226219Swpaul */
10326219Swpaulstatic struct {
10426219Swpaul	u_long ncachehits;	/* times cache hit, and is not replay */
10526219Swpaul	u_long ncachereplays;	/* times cache hit, and is replay */
10626219Swpaul	u_long ncachemisses;	/* times cache missed */
10726219Swpaul} svcauthdes_stats;
10826219Swpaul
10926219Swpaul/*
11026219Swpaul * Service side authenticator for AUTH_DES
11126219Swpaul */
11226219Swpaulenum auth_stat
11326219Swpaul_svcauth_des(rqst, msg)
11492889Sobrien	struct svc_req *rqst;
11592889Sobrien	struct rpc_msg *msg;
11626219Swpaul{
11726219Swpaul
11892889Sobrien	long *ixdr;
11926219Swpaul	des_block cryptbuf[2];
12092889Sobrien	struct authdes_cred *cred;
12126219Swpaul	struct authdes_verf verf;
12226219Swpaul	int status;
12392889Sobrien	struct cache_entry *entry;
12426219Swpaul	short sid = 0;
12526219Swpaul	des_block *sessionkey;
12626219Swpaul	des_block ivec;
12726219Swpaul	u_int window;
12826219Swpaul	struct timeval timestamp;
12926219Swpaul	u_long namelen;
13026219Swpaul	struct area {
13126219Swpaul		struct authdes_cred area_cred;
13226219Swpaul		char area_netname[MAXNETNAMELEN+1];
13326219Swpaul	} *area;
13426219Swpaul
13526219Swpaul	if (authdes_cache == NULL) {
13626219Swpaul		cache_init();
13726219Swpaul	}
13826219Swpaul
13926219Swpaul	area = (struct area *)rqst->rq_clntcred;
14026219Swpaul	cred = (struct authdes_cred *)&area->area_cred;
14126219Swpaul
14226219Swpaul	/*
14326219Swpaul	 * Get the credential
14426219Swpaul	 */
14526219Swpaul	ixdr = (long *)msg->rm_call.cb_cred.oa_base;
14626219Swpaul	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
14726219Swpaul	switch (cred->adc_namekind) {
14826219Swpaul	case ADN_FULLNAME:
14926219Swpaul		namelen = IXDR_GET_U_LONG(ixdr);
15026219Swpaul		if (namelen > MAXNETNAMELEN) {
15126219Swpaul			return (AUTH_BADCRED);
15226219Swpaul		}
15326219Swpaul		cred->adc_fullname.name = area->area_netname;
15426219Swpaul		bcopy((char *)ixdr, cred->adc_fullname.name,
15526219Swpaul			(u_int)namelen);
15626219Swpaul		cred->adc_fullname.name[namelen] = 0;
15726219Swpaul		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
15826219Swpaul		cred->adc_fullname.key.key.high = (u_long)*ixdr++;
15926219Swpaul		cred->adc_fullname.key.key.low = (u_long)*ixdr++;
16026219Swpaul		cred->adc_fullname.window = (u_long)*ixdr++;
16126219Swpaul		break;
16226219Swpaul	case ADN_NICKNAME:
16326219Swpaul		cred->adc_nickname = (u_long)*ixdr++;
16426219Swpaul		break;
16526219Swpaul	default:
16626219Swpaul		return (AUTH_BADCRED);
16726219Swpaul	}
16826219Swpaul
16926219Swpaul	/*
17026219Swpaul	 * Get the verifier
17126219Swpaul	 */
17226219Swpaul	ixdr = (long *)msg->rm_call.cb_verf.oa_base;
17326219Swpaul	verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
17426219Swpaul	verf.adv_xtimestamp.key.low =  (u_long)*ixdr++;
17526219Swpaul	verf.adv_int_u = (u_long)*ixdr++;
17626219Swpaul
17726219Swpaul
17826219Swpaul	/*
17926219Swpaul	 * Get the conversation key
18026219Swpaul 	 */
18126219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
18226219Swpaul		netobj pkey;
18326219Swpaul		char pkey_data[1024];
18426219Swpaul
18526219Swpaul		sessionkey = &cred->adc_fullname.key;
18626219Swpaul		if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
18726219Swpaul			debug("getpublickey");
18826219Swpaul			return(AUTH_BADCRED);
18926219Swpaul		}
19026219Swpaul		pkey.n_bytes = pkey_data;
19126219Swpaul		pkey.n_len = strlen(pkey_data) + 1;
19226219Swpaul		if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
19326219Swpaul				       sessionkey) < 0) {
19426219Swpaul			debug("decryptsessionkey");
19526219Swpaul			return (AUTH_BADCRED); /* key not found */
19626219Swpaul		}
19726219Swpaul	} else { /* ADN_NICKNAME */
19826219Swpaul		sid = (short)cred->adc_nickname;
19974462Salfred		if (sid < 0 || sid >= AUTHDES_CACHESZ) {
20026219Swpaul			debug("bad nickname");
20126219Swpaul			return (AUTH_BADCRED);	/* garbled credential */
20226219Swpaul		}
20326219Swpaul		sessionkey = &authdes_cache[sid].key;
20426219Swpaul	}
20526219Swpaul
20626219Swpaul
20726219Swpaul	/*
20826219Swpaul	 * Decrypt the timestamp
20926219Swpaul	 */
21026219Swpaul	cryptbuf[0] = verf.adv_xtimestamp;
21126219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
21226219Swpaul		cryptbuf[1].key.high = cred->adc_fullname.window;
21326219Swpaul		cryptbuf[1].key.low = verf.adv_winverf;
21426219Swpaul		ivec.key.high = ivec.key.low = 0;
21526219Swpaul		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
21626219Swpaul			2*sizeof(des_block), DES_DECRYPT | DES_HW,
21726219Swpaul			(char *)&ivec);
21826219Swpaul	} else {
21926219Swpaul		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
22026219Swpaul			sizeof(des_block), DES_DECRYPT | DES_HW);
22126219Swpaul	}
22226219Swpaul	if (DES_FAILED(status)) {
22326219Swpaul		debug("decryption failure");
22426219Swpaul		return (AUTH_FAILED);	/* system error */
22526219Swpaul	}
22626219Swpaul
22726219Swpaul	/*
22826219Swpaul	 * XDR the decrypted timestamp
22926219Swpaul	 */
23026219Swpaul	ixdr = (long *)cryptbuf;
23126219Swpaul	timestamp.tv_sec = IXDR_GET_LONG(ixdr);
23226219Swpaul	timestamp.tv_usec = IXDR_GET_LONG(ixdr);
23326219Swpaul
23426219Swpaul	/*
23526219Swpaul 	 * Check for valid credentials and verifiers.
23626219Swpaul	 * They could be invalid because the key was flushed
23726219Swpaul	 * out of the cache, and so a new session should begin.
23826219Swpaul	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
23926219Swpaul	 */
24026219Swpaul	{
24126219Swpaul		struct timeval current;
24226219Swpaul		int nick;
24326219Swpaul		int winverf;
24426219Swpaul
24526219Swpaul		if (cred->adc_namekind == ADN_FULLNAME) {
24626219Swpaul			window = IXDR_GET_U_LONG(ixdr);
24726219Swpaul			winverf = IXDR_GET_U_LONG(ixdr);
24826219Swpaul			if (winverf != window - 1) {
24926219Swpaul				debug("window verifier mismatch");
25026219Swpaul				return (AUTH_BADCRED);	/* garbled credential */
25126219Swpaul			}
25226219Swpaul			sid = cache_spot(sessionkey, cred->adc_fullname.name,
25326219Swpaul			    &timestamp);
25426219Swpaul			if (sid < 0) {
25526219Swpaul				debug("replayed credential");
25626219Swpaul				return (AUTH_REJECTEDCRED);	/* replay */
25726219Swpaul			}
25826219Swpaul			nick = 0;
25926219Swpaul		} else {	/* ADN_NICKNAME */
26026219Swpaul			window = authdes_cache[sid].window;
26126219Swpaul			nick = 1;
26226219Swpaul		}
26326219Swpaul
26426219Swpaul		if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
26526219Swpaul			debug("invalid usecs");
26626219Swpaul			/* cached out (bad key), or garbled verifier */
26726219Swpaul			return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
26826219Swpaul		}
26926219Swpaul		if (nick && BEFORE(&timestamp,
27026219Swpaul				   &authdes_cache[sid].laststamp)) {
27126219Swpaul			debug("timestamp before last seen");
27226219Swpaul			return (AUTH_REJECTEDVERF);	/* replay */
27326219Swpaul		}
274239991Sed		(void)gettimeofday(&current, NULL);
27526219Swpaul		current.tv_sec -= window;	/* allow for expiration */
27626219Swpaul		if (!BEFORE(&current, &timestamp)) {
27726219Swpaul			debug("timestamp expired");
27826219Swpaul			/* replay, or garbled credential */
27926219Swpaul			return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
28026219Swpaul		}
28126219Swpaul	}
28226219Swpaul
28326219Swpaul	/*
28426219Swpaul	 * Set up the reply verifier
28526219Swpaul	 */
28626219Swpaul	verf.adv_nickname = (u_long)sid;
28726219Swpaul
28826219Swpaul	/*
28926219Swpaul	 * xdr the timestamp before encrypting
29026219Swpaul	 */
29126219Swpaul	ixdr = (long *)cryptbuf;
29226219Swpaul	IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
29326219Swpaul	IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
29426219Swpaul
29526219Swpaul	/*
29626219Swpaul	 * encrypt the timestamp
29726219Swpaul	 */
29826219Swpaul	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
29926219Swpaul	    sizeof(des_block), DES_ENCRYPT | DES_HW);
30026219Swpaul	if (DES_FAILED(status)) {
30126219Swpaul		debug("encryption failure");
30226219Swpaul		return (AUTH_FAILED);	/* system error */
30326219Swpaul	}
30426219Swpaul	verf.adv_xtimestamp = cryptbuf[0];
30526219Swpaul
30626219Swpaul	/*
30726219Swpaul	 * Serialize the reply verifier, and update rqst
30826219Swpaul	 */
30926219Swpaul	ixdr = (long *)msg->rm_call.cb_verf.oa_base;
31026219Swpaul	*ixdr++ = (long)verf.adv_xtimestamp.key.high;
31126219Swpaul	*ixdr++ = (long)verf.adv_xtimestamp.key.low;
31226219Swpaul	*ixdr++ = (long)verf.adv_int_u;
31326219Swpaul
31426219Swpaul	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
31526219Swpaul	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
31626219Swpaul	rqst->rq_xprt->xp_verf.oa_length =
31726219Swpaul		(char *)ixdr - msg->rm_call.cb_verf.oa_base;
31826219Swpaul
31926219Swpaul	/*
32026219Swpaul	 * We succeeded, commit the data to the cache now and
32126219Swpaul	 * finish cooking the credential.
32226219Swpaul	 */
32326219Swpaul	entry = &authdes_cache[sid];
32426219Swpaul	entry->laststamp = timestamp;
32526219Swpaul	cache_ref(sid);
32626219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
32726219Swpaul		cred->adc_fullname.window = window;
32826219Swpaul		cred->adc_nickname = (u_long)sid;	/* save nickname */
32926219Swpaul		if (entry->rname != NULL) {
33026219Swpaul			mem_free(entry->rname, strlen(entry->rname) + 1);
33126219Swpaul		}
33226219Swpaul		entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
33326219Swpaul					 + 1);
33426219Swpaul		if (entry->rname != NULL) {
33526219Swpaul			(void) strcpy(entry->rname, cred->adc_fullname.name);
33626219Swpaul		} else {
33726219Swpaul			debug("out of memory");
33826219Swpaul		}
33926219Swpaul		entry->key = *sessionkey;
34026219Swpaul		entry->window = window;
34126219Swpaul		invalidate(entry->localcred); /* mark any cached cred invalid */
34226219Swpaul	} else { /* ADN_NICKNAME */
34326219Swpaul		/*
34426219Swpaul		 * nicknames are cooked into fullnames
34526219Swpaul		 */
34626219Swpaul		cred->adc_namekind = ADN_FULLNAME;
34726219Swpaul		cred->adc_fullname.name = entry->rname;
34826219Swpaul		cred->adc_fullname.key = entry->key;
34926219Swpaul		cred->adc_fullname.window = entry->window;
35026219Swpaul	}
35126219Swpaul	return (AUTH_OK);	/* we made it!*/
35226219Swpaul}
35326219Swpaul
35426219Swpaul
35526219Swpaul/*
35626219Swpaul * Initialize the cache
35726219Swpaul */
35826219Swpaulstatic void
35926219Swpaulcache_init()
36026219Swpaul{
36192889Sobrien	int i;
36226219Swpaul
36326219Swpaul	authdes_cache = (struct cache_entry *)
36426219Swpaul		mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);
36526219Swpaul	bzero((char *)authdes_cache,
36626219Swpaul		sizeof(struct cache_entry) * AUTHDES_CACHESZ);
36726219Swpaul
36826219Swpaul	authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
36926219Swpaul	/*
37026219Swpaul	 * Initialize the lru list
37126219Swpaul	 */
37226219Swpaul	for (i = 0; i < AUTHDES_CACHESZ; i++) {
37326219Swpaul		authdes_lru[i] = i;
37426219Swpaul	}
37526219Swpaul}
37626219Swpaul
37726219Swpaul
37826219Swpaul/*
37926219Swpaul * Find the lru victim
38026219Swpaul */
38126219Swpaulstatic short
38226219Swpaulcache_victim()
38326219Swpaul{
38426219Swpaul	return (authdes_lru[AUTHDES_CACHESZ-1]);
38526219Swpaul}
38626219Swpaul
38726219Swpaul/*
38826219Swpaul * Note that sid was referenced
38926219Swpaul */
39026219Swpaulstatic void
39126219Swpaulcache_ref(sid)
39292889Sobrien	short sid;
39326219Swpaul{
39492889Sobrien	int i;
39592889Sobrien	short curr;
39692889Sobrien	short prev;
39726219Swpaul
39826219Swpaul	prev = authdes_lru[0];
39926219Swpaul	authdes_lru[0] = sid;
40026219Swpaul	for (i = 1; prev != sid; i++) {
40126219Swpaul		curr = authdes_lru[i];
40226219Swpaul		authdes_lru[i] = prev;
40326219Swpaul		prev = curr;
40426219Swpaul	}
40526219Swpaul}
40626219Swpaul
40726219Swpaul
40826219Swpaul/*
40926219Swpaul * Find a spot in the cache for a credential containing
41026219Swpaul * the items given.  Return -1 if a replay is detected, otherwise
41126219Swpaul * return the spot in the cache.
41226219Swpaul */
41326219Swpaulstatic short
41426219Swpaulcache_spot(key, name, timestamp)
41592889Sobrien	des_block *key;
41626219Swpaul	char *name;
41726219Swpaul	struct timeval *timestamp;
41826219Swpaul{
41992889Sobrien	struct cache_entry *cp;
42092889Sobrien	int i;
42192889Sobrien	u_long hi;
42226219Swpaul
42326219Swpaul	hi = key->key.high;
42426219Swpaul	for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
42526219Swpaul		if (cp->key.key.high == hi &&
42626219Swpaul		    cp->key.key.low == key->key.low &&
42726219Swpaul		    cp->rname != NULL &&
42826219Swpaul		    bcmp(cp->rname, name, strlen(name) + 1) == 0) {
42926219Swpaul			if (BEFORE(timestamp, &cp->laststamp)) {
43026219Swpaul				svcauthdes_stats.ncachereplays++;
43126219Swpaul				return (-1); /* replay */
43226219Swpaul			}
43326219Swpaul			svcauthdes_stats.ncachehits++;
43426219Swpaul			return (i);	/* refresh */
43526219Swpaul		}
43626219Swpaul	}
43726219Swpaul	svcauthdes_stats.ncachemisses++;
43826219Swpaul	return (cache_victim());	/* new credential */
43926219Swpaul}
44026219Swpaul
44126219Swpaul
44226219Swpaul#if (defined(sun) || defined(vax) || defined(__FreeBSD__))
44326219Swpaul/*
44426219Swpaul * Local credential handling stuff.
44526219Swpaul * NOTE: bsd unix dependent.
44626219Swpaul * Other operating systems should put something else here.
44726219Swpaul */
44826219Swpaul#define UNKNOWN 	-2	/* grouplen, if cached cred is unknown user */
44926219Swpaul#define INVALID		-1 	/* grouplen, if cache entry is invalid */
45026219Swpaul
45126219Swpaulstruct bsdcred {
452201959Sbrooks	uid_t uid;		/* cached uid */
453201959Sbrooks	gid_t gid;		/* cached gid */
454201959Sbrooks	int grouplen;	/* length of cached groups */
455201959Sbrooks	gid_t groups[NGRPS];	/* cached groups */
45626219Swpaul};
45726219Swpaul
45826219Swpaul/*
45926219Swpaul * Map a des credential into a unix cred.
46026219Swpaul * We cache the credential here so the application does
46126219Swpaul * not have to make an rpc call every time to interpret
46226219Swpaul * the credential.
46326219Swpaul */
46426219Swpaulint
46526219Swpaulauthdes_getucred(adc, uid, gid, grouplen, groups)
46626219Swpaul	struct authdes_cred *adc;
46726219Swpaul	uid_t *uid;
46826219Swpaul	gid_t *gid;
46926219Swpaul	int *grouplen;
47092889Sobrien	gid_t *groups;
47126219Swpaul{
47226219Swpaul	unsigned sid;
47392889Sobrien	int i;
47426219Swpaul	uid_t i_uid;
47526219Swpaul	gid_t i_gid;
47626219Swpaul	int i_grouplen;
47726219Swpaul	struct bsdcred *cred;
47826219Swpaul
47926219Swpaul	sid = adc->adc_nickname;
48026219Swpaul	if (sid >= AUTHDES_CACHESZ) {
48126219Swpaul		debug("invalid nickname");
48226219Swpaul		return (0);
48326219Swpaul	}
48426219Swpaul	cred = (struct bsdcred *)authdes_cache[sid].localcred;
48526219Swpaul	if (cred == NULL) {
48626219Swpaul		cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
48726219Swpaul		authdes_cache[sid].localcred = (char *)cred;
48826219Swpaul		cred->grouplen = INVALID;
48926219Swpaul	}
49026219Swpaul	if (cred->grouplen == INVALID) {
49126219Swpaul		/*
49226219Swpaul		 * not in cache: lookup
49326219Swpaul		 */
49426219Swpaul		if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
49526219Swpaul			&i_grouplen, groups))
49626219Swpaul		{
49726219Swpaul			debug("unknown netname");
49826219Swpaul			cred->grouplen = UNKNOWN;	/* mark as lookup up, but not found */
49926219Swpaul			return (0);
50026219Swpaul		}
50126219Swpaul		debug("missed ucred cache");
50226219Swpaul		*uid = cred->uid = i_uid;
50326219Swpaul		*gid = cred->gid = i_gid;
50426219Swpaul		*grouplen = cred->grouplen = i_grouplen;
50526219Swpaul		for (i = i_grouplen - 1; i >= 0; i--) {
50626219Swpaul			cred->groups[i] = groups[i]; /* int to short */
50726219Swpaul		}
50826219Swpaul		return (1);
50926219Swpaul	} else if (cred->grouplen == UNKNOWN) {
51026219Swpaul		/*
51126219Swpaul		 * Already lookup up, but no match found
51226219Swpaul		 */
51326219Swpaul		return (0);
51426219Swpaul	}
51526219Swpaul
51626219Swpaul	/*
51726219Swpaul	 * cached credentials
51826219Swpaul	 */
51926219Swpaul	*uid = cred->uid;
52026219Swpaul	*gid = cred->gid;
52126219Swpaul	*grouplen = cred->grouplen;
52226219Swpaul	for (i = cred->grouplen - 1; i >= 0; i--) {
52326219Swpaul		groups[i] = cred->groups[i];	/* short to int */
52426219Swpaul	}
52526219Swpaul	return (1);
52626219Swpaul}
52726219Swpaul
52826219Swpaulstatic void
52926219Swpaulinvalidate(cred)
53026219Swpaul	char *cred;
53126219Swpaul{
53226219Swpaul	if (cred == NULL) {
53326219Swpaul		return;
53426219Swpaul	}
53526219Swpaul	((struct bsdcred *)cred)->grouplen = INVALID;
53626219Swpaul}
53726219Swpaul#endif
53826219Swpaul
539