126219Swpaul
226219Swpaul/*
326219Swpaul * Copyright (c) 1988 by Sun Microsystems, Inc.
426219Swpaul */
526219Swpaul
6261057Smav/*-
7261057Smav * Copyright (c) 2009, Sun Microsystems, Inc.
8261057Smav * All rights reserved.
9261057Smav *
10261057Smav * Redistribution and use in source and binary forms, with or without
11261057Smav * modification, are permitted provided that the following conditions are met:
12261057Smav * - Redistributions of source code must retain the above copyright notice,
13261057Smav *   this list of conditions and the following disclaimer.
14261057Smav * - Redistributions in binary form must reproduce the above copyright notice,
15261057Smav *   this list of conditions and the following disclaimer in the documentation
16261057Smav *   and/or other materials provided with the distribution.
17261057Smav * - Neither the name of Sun Microsystems, Inc. nor the names of its
18261057Smav *   contributors may be used to endorse or promote products derived
19261057Smav *   from this software without specific prior written permission.
2026219Swpaul *
21261057Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22261057Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23261057Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24261057Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25261057Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26261057Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27261057Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28261057Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29261057Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30261057Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31261057Smav * POSSIBILITY OF SUCH DAMAGE.
3226219Swpaul */
3326219Swpaul
3426219Swpaul/*
3526219Swpaul * svcauth_des.c, server-side des authentication
3626219Swpaul *
3726219Swpaul * We insure for the service the following:
3826219Swpaul * (1) The timestamp microseconds do not exceed 1 million.
3926219Swpaul * (2) The timestamp plus the window is less than the current time.
4026219Swpaul * (3) The timestamp is not less than the one previously
4126219Swpaul *     seen in the current session.
4226219Swpaul *
4326219Swpaul * It is up to the server to determine if the window size is
4426219Swpaul * too small .
4526219Swpaul *
4626219Swpaul */
4726219Swpaul
4875094Siedowse#include "namespace.h"
4974462Salfred#include "reentrant.h"
5026219Swpaul#include <string.h>
5126219Swpaul#include <stdlib.h>
5271579Sdeischen#include <stdio.h>
5326219Swpaul#include <unistd.h>
5426219Swpaul#include <rpc/des_crypt.h>
5526219Swpaul#include <sys/param.h>
5626219Swpaul#include <netinet/in.h>
5726219Swpaul#include <rpc/types.h>
5826219Swpaul#include <rpc/xdr.h>
5926219Swpaul#include <rpc/auth.h>
6026219Swpaul#include <rpc/auth_des.h>
6126219Swpaul#include <rpc/svc.h>
6226219Swpaul#include <rpc/rpc_msg.h>
6326219Swpaul#include <rpc/svc_auth.h>
6474462Salfred#include "libc_private.h"
6526219Swpaul
6626219Swpaul#if defined(LIBC_SCCS) && !defined(lint)
6792990Sobrienstatic char sccsid[] = 	"@(#)svcauth_des.c	2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI";
6826219Swpaul#endif
6992990Sobrien#include <sys/cdefs.h>
7092990Sobrien__FBSDID("$FreeBSD$");
7126219Swpaul
7290271Salfredextern int key_decryptsession_pk(const char *, netobj *, des_block *);
7390271Salfred
7426219Swpaul#define debug(msg)	 printf("svcauth_des: %s\n", msg)
7526219Swpaul
7626219Swpaul#define USEC_PER_SEC ((u_long) 1000000L)
7726219Swpaul#define BEFORE(t1, t2) timercmp(t1, t2, <)
7826219Swpaul
7926219Swpaul/*
8026219Swpaul * LRU cache of conversation keys and some other useful items.
8126219Swpaul */
8226219Swpaul#define AUTHDES_CACHESZ 64
8326219Swpaulstruct cache_entry {
8426219Swpaul	des_block key;		/* conversation key */
8526219Swpaul	char *rname;		/* client's name */
8626219Swpaul	u_int window;		/* credential lifetime window */
8726219Swpaul	struct timeval laststamp;	/* detect replays of creds */
8826219Swpaul	char *localcred;	/* generic local credential */
8926219Swpaul};
9026219Swpaulstatic struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
9126219Swpaulstatic short *authdes_lru/* [AUTHDES_CACHESZ] */;
9226219Swpaul
9326219Swpaulstatic void cache_init();	/* initialize the cache */
9426219Swpaulstatic short cache_spot();	/* find an entry in the cache */
9526219Swpaulstatic void cache_ref(/*short sid*/);	/* note that sid was ref'd */
9626219Swpaul
9726219Swpaulstatic void invalidate();	/* invalidate entry in cache */
9826219Swpaul
9926219Swpaul/*
10026219Swpaul * cache statistics
10126219Swpaul */
10226219Swpaulstatic struct {
10326219Swpaul	u_long ncachehits;	/* times cache hit, and is not replay */
10426219Swpaul	u_long ncachereplays;	/* times cache hit, and is replay */
10526219Swpaul	u_long ncachemisses;	/* times cache missed */
10626219Swpaul} svcauthdes_stats;
10726219Swpaul
10826219Swpaul/*
10926219Swpaul * Service side authenticator for AUTH_DES
11026219Swpaul */
11126219Swpaulenum auth_stat
11226219Swpaul_svcauth_des(rqst, msg)
11392889Sobrien	struct svc_req *rqst;
11492889Sobrien	struct rpc_msg *msg;
11526219Swpaul{
11626219Swpaul
11792889Sobrien	long *ixdr;
11826219Swpaul	des_block cryptbuf[2];
11992889Sobrien	struct authdes_cred *cred;
12026219Swpaul	struct authdes_verf verf;
12126219Swpaul	int status;
12292889Sobrien	struct cache_entry *entry;
12326219Swpaul	short sid = 0;
12426219Swpaul	des_block *sessionkey;
12526219Swpaul	des_block ivec;
12626219Swpaul	u_int window;
12726219Swpaul	struct timeval timestamp;
12826219Swpaul	u_long namelen;
12926219Swpaul	struct area {
13026219Swpaul		struct authdes_cred area_cred;
13126219Swpaul		char area_netname[MAXNETNAMELEN+1];
13226219Swpaul	} *area;
13326219Swpaul
13426219Swpaul	if (authdes_cache == NULL) {
13526219Swpaul		cache_init();
13626219Swpaul	}
13726219Swpaul
13826219Swpaul	area = (struct area *)rqst->rq_clntcred;
13926219Swpaul	cred = (struct authdes_cred *)&area->area_cred;
14026219Swpaul
14126219Swpaul	/*
14226219Swpaul	 * Get the credential
14326219Swpaul	 */
14426219Swpaul	ixdr = (long *)msg->rm_call.cb_cred.oa_base;
14526219Swpaul	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
14626219Swpaul	switch (cred->adc_namekind) {
14726219Swpaul	case ADN_FULLNAME:
14826219Swpaul		namelen = IXDR_GET_U_LONG(ixdr);
14926219Swpaul		if (namelen > MAXNETNAMELEN) {
15026219Swpaul			return (AUTH_BADCRED);
15126219Swpaul		}
15226219Swpaul		cred->adc_fullname.name = area->area_netname;
15326219Swpaul		bcopy((char *)ixdr, cred->adc_fullname.name,
15426219Swpaul			(u_int)namelen);
15526219Swpaul		cred->adc_fullname.name[namelen] = 0;
15626219Swpaul		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
15726219Swpaul		cred->adc_fullname.key.key.high = (u_long)*ixdr++;
15826219Swpaul		cred->adc_fullname.key.key.low = (u_long)*ixdr++;
15926219Swpaul		cred->adc_fullname.window = (u_long)*ixdr++;
16026219Swpaul		break;
16126219Swpaul	case ADN_NICKNAME:
16226219Swpaul		cred->adc_nickname = (u_long)*ixdr++;
16326219Swpaul		break;
16426219Swpaul	default:
16526219Swpaul		return (AUTH_BADCRED);
16626219Swpaul	}
16726219Swpaul
16826219Swpaul	/*
16926219Swpaul	 * Get the verifier
17026219Swpaul	 */
17126219Swpaul	ixdr = (long *)msg->rm_call.cb_verf.oa_base;
17226219Swpaul	verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
17326219Swpaul	verf.adv_xtimestamp.key.low =  (u_long)*ixdr++;
17426219Swpaul	verf.adv_int_u = (u_long)*ixdr++;
17526219Swpaul
17626219Swpaul
17726219Swpaul	/*
17826219Swpaul	 * Get the conversation key
17926219Swpaul 	 */
18026219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
18126219Swpaul		netobj pkey;
18226219Swpaul		char pkey_data[1024];
18326219Swpaul
18426219Swpaul		sessionkey = &cred->adc_fullname.key;
18526219Swpaul		if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
18626219Swpaul			debug("getpublickey");
18726219Swpaul			return(AUTH_BADCRED);
18826219Swpaul		}
18926219Swpaul		pkey.n_bytes = pkey_data;
19026219Swpaul		pkey.n_len = strlen(pkey_data) + 1;
19126219Swpaul		if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
19226219Swpaul				       sessionkey) < 0) {
19326219Swpaul			debug("decryptsessionkey");
19426219Swpaul			return (AUTH_BADCRED); /* key not found */
19526219Swpaul		}
19626219Swpaul	} else { /* ADN_NICKNAME */
19726219Swpaul		sid = (short)cred->adc_nickname;
19874462Salfred		if (sid < 0 || sid >= AUTHDES_CACHESZ) {
19926219Swpaul			debug("bad nickname");
20026219Swpaul			return (AUTH_BADCRED);	/* garbled credential */
20126219Swpaul		}
20226219Swpaul		sessionkey = &authdes_cache[sid].key;
20326219Swpaul	}
20426219Swpaul
20526219Swpaul
20626219Swpaul	/*
20726219Swpaul	 * Decrypt the timestamp
20826219Swpaul	 */
20926219Swpaul	cryptbuf[0] = verf.adv_xtimestamp;
21026219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
21126219Swpaul		cryptbuf[1].key.high = cred->adc_fullname.window;
21226219Swpaul		cryptbuf[1].key.low = verf.adv_winverf;
21326219Swpaul		ivec.key.high = ivec.key.low = 0;
21426219Swpaul		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
21526219Swpaul			2*sizeof(des_block), DES_DECRYPT | DES_HW,
21626219Swpaul			(char *)&ivec);
21726219Swpaul	} else {
21826219Swpaul		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
21926219Swpaul			sizeof(des_block), DES_DECRYPT | DES_HW);
22026219Swpaul	}
22126219Swpaul	if (DES_FAILED(status)) {
22226219Swpaul		debug("decryption failure");
22326219Swpaul		return (AUTH_FAILED);	/* system error */
22426219Swpaul	}
22526219Swpaul
22626219Swpaul	/*
22726219Swpaul	 * XDR the decrypted timestamp
22826219Swpaul	 */
22926219Swpaul	ixdr = (long *)cryptbuf;
23026219Swpaul	timestamp.tv_sec = IXDR_GET_LONG(ixdr);
23126219Swpaul	timestamp.tv_usec = IXDR_GET_LONG(ixdr);
23226219Swpaul
23326219Swpaul	/*
23426219Swpaul 	 * Check for valid credentials and verifiers.
23526219Swpaul	 * They could be invalid because the key was flushed
23626219Swpaul	 * out of the cache, and so a new session should begin.
23726219Swpaul	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
23826219Swpaul	 */
23926219Swpaul	{
24026219Swpaul		struct timeval current;
24126219Swpaul		int nick;
24226219Swpaul		int winverf;
24326219Swpaul
24426219Swpaul		if (cred->adc_namekind == ADN_FULLNAME) {
24526219Swpaul			window = IXDR_GET_U_LONG(ixdr);
24626219Swpaul			winverf = IXDR_GET_U_LONG(ixdr);
24726219Swpaul			if (winverf != window - 1) {
24826219Swpaul				debug("window verifier mismatch");
24926219Swpaul				return (AUTH_BADCRED);	/* garbled credential */
25026219Swpaul			}
25126219Swpaul			sid = cache_spot(sessionkey, cred->adc_fullname.name,
25226219Swpaul			    &timestamp);
25326219Swpaul			if (sid < 0) {
25426219Swpaul				debug("replayed credential");
25526219Swpaul				return (AUTH_REJECTEDCRED);	/* replay */
25626219Swpaul			}
25726219Swpaul			nick = 0;
25826219Swpaul		} else {	/* ADN_NICKNAME */
25926219Swpaul			window = authdes_cache[sid].window;
26026219Swpaul			nick = 1;
26126219Swpaul		}
26226219Swpaul
26326219Swpaul		if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
26426219Swpaul			debug("invalid usecs");
26526219Swpaul			/* cached out (bad key), or garbled verifier */
26626219Swpaul			return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
26726219Swpaul		}
26826219Swpaul		if (nick && BEFORE(&timestamp,
26926219Swpaul				   &authdes_cache[sid].laststamp)) {
27026219Swpaul			debug("timestamp before last seen");
27126219Swpaul			return (AUTH_REJECTEDVERF);	/* replay */
27226219Swpaul		}
27326219Swpaul		(void) gettimeofday(&current, (struct timezone *)NULL);
27426219Swpaul		current.tv_sec -= window;	/* allow for expiration */
27526219Swpaul		if (!BEFORE(&current, &timestamp)) {
27626219Swpaul			debug("timestamp expired");
27726219Swpaul			/* replay, or garbled credential */
27826219Swpaul			return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
27926219Swpaul		}
28026219Swpaul	}
28126219Swpaul
28226219Swpaul	/*
28326219Swpaul	 * Set up the reply verifier
28426219Swpaul	 */
28526219Swpaul	verf.adv_nickname = (u_long)sid;
28626219Swpaul
28726219Swpaul	/*
28826219Swpaul	 * xdr the timestamp before encrypting
28926219Swpaul	 */
29026219Swpaul	ixdr = (long *)cryptbuf;
29126219Swpaul	IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
29226219Swpaul	IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
29326219Swpaul
29426219Swpaul	/*
29526219Swpaul	 * encrypt the timestamp
29626219Swpaul	 */
29726219Swpaul	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
29826219Swpaul	    sizeof(des_block), DES_ENCRYPT | DES_HW);
29926219Swpaul	if (DES_FAILED(status)) {
30026219Swpaul		debug("encryption failure");
30126219Swpaul		return (AUTH_FAILED);	/* system error */
30226219Swpaul	}
30326219Swpaul	verf.adv_xtimestamp = cryptbuf[0];
30426219Swpaul
30526219Swpaul	/*
30626219Swpaul	 * Serialize the reply verifier, and update rqst
30726219Swpaul	 */
30826219Swpaul	ixdr = (long *)msg->rm_call.cb_verf.oa_base;
30926219Swpaul	*ixdr++ = (long)verf.adv_xtimestamp.key.high;
31026219Swpaul	*ixdr++ = (long)verf.adv_xtimestamp.key.low;
31126219Swpaul	*ixdr++ = (long)verf.adv_int_u;
31226219Swpaul
31326219Swpaul	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
31426219Swpaul	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
31526219Swpaul	rqst->rq_xprt->xp_verf.oa_length =
31626219Swpaul		(char *)ixdr - msg->rm_call.cb_verf.oa_base;
31726219Swpaul
31826219Swpaul	/*
31926219Swpaul	 * We succeeded, commit the data to the cache now and
32026219Swpaul	 * finish cooking the credential.
32126219Swpaul	 */
32226219Swpaul	entry = &authdes_cache[sid];
32326219Swpaul	entry->laststamp = timestamp;
32426219Swpaul	cache_ref(sid);
32526219Swpaul	if (cred->adc_namekind == ADN_FULLNAME) {
32626219Swpaul		cred->adc_fullname.window = window;
32726219Swpaul		cred->adc_nickname = (u_long)sid;	/* save nickname */
32826219Swpaul		if (entry->rname != NULL) {
32926219Swpaul			mem_free(entry->rname, strlen(entry->rname) + 1);
33026219Swpaul		}
33126219Swpaul		entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
33226219Swpaul					 + 1);
33326219Swpaul		if (entry->rname != NULL) {
33426219Swpaul			(void) strcpy(entry->rname, cred->adc_fullname.name);
33526219Swpaul		} else {
33626219Swpaul			debug("out of memory");
33726219Swpaul		}
33826219Swpaul		entry->key = *sessionkey;
33926219Swpaul		entry->window = window;
34026219Swpaul		invalidate(entry->localcred); /* mark any cached cred invalid */
34126219Swpaul	} else { /* ADN_NICKNAME */
34226219Swpaul		/*
34326219Swpaul		 * nicknames are cooked into fullnames
34426219Swpaul		 */
34526219Swpaul		cred->adc_namekind = ADN_FULLNAME;
34626219Swpaul		cred->adc_fullname.name = entry->rname;
34726219Swpaul		cred->adc_fullname.key = entry->key;
34826219Swpaul		cred->adc_fullname.window = entry->window;
34926219Swpaul	}
35026219Swpaul	return (AUTH_OK);	/* we made it!*/
35126219Swpaul}
35226219Swpaul
35326219Swpaul
35426219Swpaul/*
35526219Swpaul * Initialize the cache
35626219Swpaul */
35726219Swpaulstatic void
35826219Swpaulcache_init()
35926219Swpaul{
36092889Sobrien	int i;
36126219Swpaul
36226219Swpaul	authdes_cache = (struct cache_entry *)
36326219Swpaul		mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);
36426219Swpaul	bzero((char *)authdes_cache,
36526219Swpaul		sizeof(struct cache_entry) * AUTHDES_CACHESZ);
36626219Swpaul
36726219Swpaul	authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
36826219Swpaul	/*
36926219Swpaul	 * Initialize the lru list
37026219Swpaul	 */
37126219Swpaul	for (i = 0; i < AUTHDES_CACHESZ; i++) {
37226219Swpaul		authdes_lru[i] = i;
37326219Swpaul	}
37426219Swpaul}
37526219Swpaul
37626219Swpaul
37726219Swpaul/*
37826219Swpaul * Find the lru victim
37926219Swpaul */
38026219Swpaulstatic short
38126219Swpaulcache_victim()
38226219Swpaul{
38326219Swpaul	return (authdes_lru[AUTHDES_CACHESZ-1]);
38426219Swpaul}
38526219Swpaul
38626219Swpaul/*
38726219Swpaul * Note that sid was referenced
38826219Swpaul */
38926219Swpaulstatic void
39026219Swpaulcache_ref(sid)
39192889Sobrien	short sid;
39226219Swpaul{
39392889Sobrien	int i;
39492889Sobrien	short curr;
39592889Sobrien	short prev;
39626219Swpaul
39726219Swpaul	prev = authdes_lru[0];
39826219Swpaul	authdes_lru[0] = sid;
39926219Swpaul	for (i = 1; prev != sid; i++) {
40026219Swpaul		curr = authdes_lru[i];
40126219Swpaul		authdes_lru[i] = prev;
40226219Swpaul		prev = curr;
40326219Swpaul	}
40426219Swpaul}
40526219Swpaul
40626219Swpaul
40726219Swpaul/*
40826219Swpaul * Find a spot in the cache for a credential containing
40926219Swpaul * the items given.  Return -1 if a replay is detected, otherwise
41026219Swpaul * return the spot in the cache.
41126219Swpaul */
41226219Swpaulstatic short
41326219Swpaulcache_spot(key, name, timestamp)
41492889Sobrien	des_block *key;
41526219Swpaul	char *name;
41626219Swpaul	struct timeval *timestamp;
41726219Swpaul{
41892889Sobrien	struct cache_entry *cp;
41992889Sobrien	int i;
42092889Sobrien	u_long hi;
42126219Swpaul
42226219Swpaul	hi = key->key.high;
42326219Swpaul	for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
42426219Swpaul		if (cp->key.key.high == hi &&
42526219Swpaul		    cp->key.key.low == key->key.low &&
42626219Swpaul		    cp->rname != NULL &&
42726219Swpaul		    bcmp(cp->rname, name, strlen(name) + 1) == 0) {
42826219Swpaul			if (BEFORE(timestamp, &cp->laststamp)) {
42926219Swpaul				svcauthdes_stats.ncachereplays++;
43026219Swpaul				return (-1); /* replay */
43126219Swpaul			}
43226219Swpaul			svcauthdes_stats.ncachehits++;
43326219Swpaul			return (i);	/* refresh */
43426219Swpaul		}
43526219Swpaul	}
43626219Swpaul	svcauthdes_stats.ncachemisses++;
43726219Swpaul	return (cache_victim());	/* new credential */
43826219Swpaul}
43926219Swpaul
44026219Swpaul
44126219Swpaul#if (defined(sun) || defined(vax) || defined(__FreeBSD__))
44226219Swpaul/*
44326219Swpaul * Local credential handling stuff.
44426219Swpaul * NOTE: bsd unix dependent.
44526219Swpaul * Other operating systems should put something else here.
44626219Swpaul */
44726219Swpaul#define UNKNOWN 	-2	/* grouplen, if cached cred is unknown user */
44826219Swpaul#define INVALID		-1 	/* grouplen, if cache entry is invalid */
44926219Swpaul
45026219Swpaulstruct bsdcred {
451201959Sbrooks	uid_t uid;		/* cached uid */
452201959Sbrooks	gid_t gid;		/* cached gid */
453201959Sbrooks	int grouplen;	/* length of cached groups */
454201959Sbrooks	gid_t groups[NGRPS];	/* cached groups */
45526219Swpaul};
45626219Swpaul
45726219Swpaul/*
45826219Swpaul * Map a des credential into a unix cred.
45926219Swpaul * We cache the credential here so the application does
46026219Swpaul * not have to make an rpc call every time to interpret
46126219Swpaul * the credential.
46226219Swpaul */
46326219Swpaulint
46426219Swpaulauthdes_getucred(adc, uid, gid, grouplen, groups)
46526219Swpaul	struct authdes_cred *adc;
46626219Swpaul	uid_t *uid;
46726219Swpaul	gid_t *gid;
46826219Swpaul	int *grouplen;
46992889Sobrien	gid_t *groups;
47026219Swpaul{
47126219Swpaul	unsigned sid;
47292889Sobrien	int i;
47326219Swpaul	uid_t i_uid;
47426219Swpaul	gid_t i_gid;
47526219Swpaul	int i_grouplen;
47626219Swpaul	struct bsdcred *cred;
47726219Swpaul
47826219Swpaul	sid = adc->adc_nickname;
47926219Swpaul	if (sid >= AUTHDES_CACHESZ) {
48026219Swpaul		debug("invalid nickname");
48126219Swpaul		return (0);
48226219Swpaul	}
48326219Swpaul	cred = (struct bsdcred *)authdes_cache[sid].localcred;
48426219Swpaul	if (cred == NULL) {
48526219Swpaul		cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
48626219Swpaul		authdes_cache[sid].localcred = (char *)cred;
48726219Swpaul		cred->grouplen = INVALID;
48826219Swpaul	}
48926219Swpaul	if (cred->grouplen == INVALID) {
49026219Swpaul		/*
49126219Swpaul		 * not in cache: lookup
49226219Swpaul		 */
49326219Swpaul		if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
49426219Swpaul			&i_grouplen, groups))
49526219Swpaul		{
49626219Swpaul			debug("unknown netname");
49726219Swpaul			cred->grouplen = UNKNOWN;	/* mark as lookup up, but not found */
49826219Swpaul			return (0);
49926219Swpaul		}
50026219Swpaul		debug("missed ucred cache");
50126219Swpaul		*uid = cred->uid = i_uid;
50226219Swpaul		*gid = cred->gid = i_gid;
50326219Swpaul		*grouplen = cred->grouplen = i_grouplen;
50426219Swpaul		for (i = i_grouplen - 1; i >= 0; i--) {
50526219Swpaul			cred->groups[i] = groups[i]; /* int to short */
50626219Swpaul		}
50726219Swpaul		return (1);
50826219Swpaul	} else if (cred->grouplen == UNKNOWN) {
50926219Swpaul		/*
51026219Swpaul		 * Already lookup up, but no match found
51126219Swpaul		 */
51226219Swpaul		return (0);
51326219Swpaul	}
51426219Swpaul
51526219Swpaul	/*
51626219Swpaul	 * cached credentials
51726219Swpaul	 */
51826219Swpaul	*uid = cred->uid;
51926219Swpaul	*gid = cred->gid;
52026219Swpaul	*grouplen = cred->grouplen;
52126219Swpaul	for (i = cred->grouplen - 1; i >= 0; i--) {
52226219Swpaul		groups[i] = cred->groups[i];	/* short to int */
52326219Swpaul	}
52426219Swpaul	return (1);
52526219Swpaul}
52626219Swpaul
52726219Swpaulstatic void
52826219Swpaulinvalidate(cred)
52926219Swpaul	char *cred;
53026219Swpaul{
53126219Swpaul	if (cred == NULL) {
53226219Swpaul		return;
53326219Swpaul	}
53426219Swpaul	((struct bsdcred *)cred)->grouplen = INVALID;
53526219Swpaul}
53626219Swpaul#endif
53726219Swpaul
538