xref: /titanic_50/usr/src/uts/common/rpc/sec/svcauthdes.c (revision 4b3b7fc6e1f62f5e2bee41aafc52e9234c484bc0)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5f48205beScasper  * Common Development and Distribution License (the "License").
6f48205beScasper  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*4b3b7fc6SAlex Wilson  * Copyright 2017 Joyent Inc
2367dbe2beSCasper H.S. Dik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
327c478bd9Sstevel@tonic-gate  * under license from the Regents of the University of California.
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate /*
367c478bd9Sstevel@tonic-gate  * svcauth_des.c, server-side des authentication
377c478bd9Sstevel@tonic-gate  *
387c478bd9Sstevel@tonic-gate  * We insure for the service the following:
397c478bd9Sstevel@tonic-gate  * (1) The timestamp microseconds do not exceed 1 million.
407c478bd9Sstevel@tonic-gate  * (2) The timestamp plus the window is less than the current time.
417c478bd9Sstevel@tonic-gate  * (3) The timestamp is not less than the one previously
427c478bd9Sstevel@tonic-gate  *	seen in the current session.
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  * It is up to the server to determine if the window size is
457c478bd9Sstevel@tonic-gate  * too small.
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <sys/types.h>
497c478bd9Sstevel@tonic-gate #include <sys/time.h>
507c478bd9Sstevel@tonic-gate #include <sys/systm.h>
517c478bd9Sstevel@tonic-gate #include <sys/param.h>
527c478bd9Sstevel@tonic-gate #include <sys/stream.h>
537c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
547c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
557c478bd9Sstevel@tonic-gate #include <sys/tiuser.h>
567c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
577c478bd9Sstevel@tonic-gate #include <sys/t_kuser.h>
587c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
597c478bd9Sstevel@tonic-gate #include <sys/debug.h>
607c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
617c478bd9Sstevel@tonic-gate #include <sys/time.h>
627c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
63*4b3b7fc6SAlex Wilson #include <sys/debug.h>
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate #include <rpc/types.h>
667c478bd9Sstevel@tonic-gate #include <rpc/xdr.h>
677c478bd9Sstevel@tonic-gate #include <rpc/auth.h>
687c478bd9Sstevel@tonic-gate #include <rpc/auth_des.h>
697c478bd9Sstevel@tonic-gate #include <rpc/rpc_msg.h>
707c478bd9Sstevel@tonic-gate #include <rpc/svc.h>
717c478bd9Sstevel@tonic-gate #include <rpc/svc_auth.h>
727c478bd9Sstevel@tonic-gate #include <rpc/clnt.h>
737c478bd9Sstevel@tonic-gate #include <rpc/des_crypt.h>
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #define	USEC_PER_SEC 1000000
767c478bd9Sstevel@tonic-gate #define	BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */)
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * Cache of conversation keys and some other useful items.
807c478bd9Sstevel@tonic-gate  * The hash table size is controled via authdes_cachesz variable.
817c478bd9Sstevel@tonic-gate  * The authdes_cachesz has to be the power of 2.
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate #define	AUTHDES_CACHE_TABLE_SZ 1024
847c478bd9Sstevel@tonic-gate static int authdes_cachesz = AUTHDES_CACHE_TABLE_SZ;
857c478bd9Sstevel@tonic-gate #define	HASH(key) ((key) & (authdes_cachesz - 1))
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /* low water mark for the number of cache entries */
887c478bd9Sstevel@tonic-gate static int low_cache_entries = 128;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate struct authdes_cache_entry {
917c478bd9Sstevel@tonic-gate 	uint32_t nickname;		/* nick name id */
927c478bd9Sstevel@tonic-gate 	uint32_t window;		/* credential lifetime window */
937c478bd9Sstevel@tonic-gate 	des_block key;			/* conversation key */
947c478bd9Sstevel@tonic-gate 	time_t	ref_time;		/* time referenced previously */
957c478bd9Sstevel@tonic-gate 	char *rname;			/* client's name */
967c478bd9Sstevel@tonic-gate 	caddr_t localcred;		/* generic local credential */
977c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *prev, *next;  /* hash table linked list */
987c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *lru_prev, *lru_next; /* LRU linked list */
997c478bd9Sstevel@tonic-gate 	kmutex_t lock;			/* cache entry lock */
1007c478bd9Sstevel@tonic-gate };
1017c478bd9Sstevel@tonic-gate static struct authdes_cache_entry **authdes_cache; /* [authdes_cachesz] */
1027c478bd9Sstevel@tonic-gate static struct authdes_cache_entry *lru_first = NULL;
1037c478bd9Sstevel@tonic-gate static struct authdes_cache_entry *lru_last = NULL;
1047c478bd9Sstevel@tonic-gate static kmutex_t authdes_lock;		/* cache table lock */
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate static struct kmem_cache *authdes_cache_handle;
1077c478bd9Sstevel@tonic-gate static uint32_t	Nickname = 0;
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_new(char *,
1107c478bd9Sstevel@tonic-gate 					des_block *, uint32_t);
1117c478bd9Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_get(uint32_t);
1127c478bd9Sstevel@tonic-gate static void authdes_cache_reclaim(void *);
1137c478bd9Sstevel@tonic-gate static void sweep_cache();
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate /*
1167c478bd9Sstevel@tonic-gate  * After 12 hours, check and delete cache entries that have been
1177c478bd9Sstevel@tonic-gate  * idled for more than 10 hours.
1187c478bd9Sstevel@tonic-gate  */
1197c478bd9Sstevel@tonic-gate static time_t authdes_sweep_interval = 12*60*60;
1207c478bd9Sstevel@tonic-gate static time_t authdes_cache_time = 10*60*60;
1217c478bd9Sstevel@tonic-gate static time_t authdes_last_swept = 0;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /*
1247c478bd9Sstevel@tonic-gate  * cache statistics
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate static int authdes_ncache = 0; /* number of current cached entries */
1277c478bd9Sstevel@tonic-gate static int authdes_ncachehits = 0; /* #times cache hit */
1287c478bd9Sstevel@tonic-gate static int authdes_ncachemisses = 0; /* #times cache missed */
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate #define	NOT_DEAD(ptr)   ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
1317c478bd9Sstevel@tonic-gate #define	IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0)
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate /*
1347c478bd9Sstevel@tonic-gate  * Service side authenticator for AUTH_DES
1357c478bd9Sstevel@tonic-gate  */
1367c478bd9Sstevel@tonic-gate enum auth_stat
_svcauth_des(struct svc_req * rqst,struct rpc_msg * msg)1377c478bd9Sstevel@tonic-gate _svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
1387c478bd9Sstevel@tonic-gate {
1397c478bd9Sstevel@tonic-gate 	int32_t *ixdr;
1407c478bd9Sstevel@tonic-gate 	des_block cryptbuf[2];
1417c478bd9Sstevel@tonic-gate 	struct authdes_cred *cred;
1427c478bd9Sstevel@tonic-gate 	struct authdes_verf verf;
1437c478bd9Sstevel@tonic-gate 	int status;
1447c478bd9Sstevel@tonic-gate 	des_block *sessionkey;
1457c478bd9Sstevel@tonic-gate 	des_block ivec;
1467c478bd9Sstevel@tonic-gate 	uint32_t window, winverf, namelen;
1477c478bd9Sstevel@tonic-gate 	bool_t nick;
1487c478bd9Sstevel@tonic-gate 	struct timeval timestamp, current_time;
1497c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *nick_entry;
1507c478bd9Sstevel@tonic-gate 	struct area {
1517c478bd9Sstevel@tonic-gate 		struct authdes_cred area_cred;
1527c478bd9Sstevel@tonic-gate 		char area_netname[MAXNETNAMELEN+1];
1537c478bd9Sstevel@tonic-gate 	} *area;
1547c478bd9Sstevel@tonic-gate 	timestruc_t now;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
1577c478bd9Sstevel@tonic-gate 	if (authdes_cache == NULL) {
1587c478bd9Sstevel@tonic-gate 		authdes_cache = kmem_zalloc(
1597c478bd9Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry *) * authdes_cachesz,
1607c478bd9Sstevel@tonic-gate 			KM_SLEEP);
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
1637c478bd9Sstevel@tonic-gate 
164*4b3b7fc6SAlex Wilson 	CTASSERT(sizeof (struct area) <= RQCRED_SIZE);
1657c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
1667c478bd9Sstevel@tonic-gate 	area = (struct area *)rqst->rq_clntcred;
1677c478bd9Sstevel@tonic-gate 	cred = (struct authdes_cred *)&area->area_cred;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	/*
1707c478bd9Sstevel@tonic-gate 	 * Get the credential
1717c478bd9Sstevel@tonic-gate 	 */
1727c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
1737c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
1747c478bd9Sstevel@tonic-gate 	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
1757c478bd9Sstevel@tonic-gate 	switch (cred->adc_namekind) {
1767c478bd9Sstevel@tonic-gate 	case ADN_FULLNAME:
1777c478bd9Sstevel@tonic-gate 		namelen = IXDR_GET_U_INT32(ixdr);
1787c478bd9Sstevel@tonic-gate 		if (namelen > MAXNETNAMELEN)
1797c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);
1807c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name = area->area_netname;
1817c478bd9Sstevel@tonic-gate 		bcopy(ixdr, cred->adc_fullname.name, namelen);
1827c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name[namelen] = 0;
1837c478bd9Sstevel@tonic-gate 		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
1847c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
1857c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
1867c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = (uint32_t)*ixdr++;
1877c478bd9Sstevel@tonic-gate 		nick = FALSE;
1887c478bd9Sstevel@tonic-gate 		break;
1897c478bd9Sstevel@tonic-gate 	case ADN_NICKNAME:
1907c478bd9Sstevel@tonic-gate 		cred->adc_nickname = (uint32_t)*ixdr++;
1917c478bd9Sstevel@tonic-gate 		nick = TRUE;
1927c478bd9Sstevel@tonic-gate 		break;
1937c478bd9Sstevel@tonic-gate 	default:
1947c478bd9Sstevel@tonic-gate 		return (AUTH_BADCRED);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	/*
1987c478bd9Sstevel@tonic-gate 	 * Get the verifier
1997c478bd9Sstevel@tonic-gate 	 */
2007c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
2017c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
2027c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
2037c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp.key.low =  (uint32_t)*ixdr++;
2047c478bd9Sstevel@tonic-gate 	verf.adv_int_u = (uint32_t)*ixdr++;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	/*
2087c478bd9Sstevel@tonic-gate 	 * Get the conversation key
2097c478bd9Sstevel@tonic-gate 	 */
2107c478bd9Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2117c478bd9Sstevel@tonic-gate 		sessionkey = &cred->adc_fullname.key;
2127c478bd9Sstevel@tonic-gate 		if (key_decryptsession(cred->adc_fullname.name, sessionkey) !=
2137c478bd9Sstevel@tonic-gate 		    RPC_SUCCESS) {
2147c478bd9Sstevel@tonic-gate 		    return (AUTH_BADCRED); /* key not found */
2157c478bd9Sstevel@tonic-gate 		}
2167c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2177c478bd9Sstevel@tonic-gate 		mutex_enter(&authdes_lock);
2187c478bd9Sstevel@tonic-gate 		if (!(nick_entry = authdes_cache_get(cred->adc_nickname))) {
2197c478bd9Sstevel@tonic-gate 		    RPCLOG(1, "_svcauth_des: nickname %d not in the cache\n",
2207c478bd9Sstevel@tonic-gate 						cred->adc_nickname);
2217c478bd9Sstevel@tonic-gate 		    mutex_exit(&authdes_lock);
2227c478bd9Sstevel@tonic-gate 		    return (AUTH_BADCRED);	/* need refresh */
2237c478bd9Sstevel@tonic-gate 		}
2247c478bd9Sstevel@tonic-gate 		sessionkey = &nick_entry->key;
2257c478bd9Sstevel@tonic-gate 		mutex_enter(&nick_entry->lock);
2267c478bd9Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	/*
2307c478bd9Sstevel@tonic-gate 	 * Decrypt the timestamp
2317c478bd9Sstevel@tonic-gate 	 */
2327c478bd9Sstevel@tonic-gate 	cryptbuf[0] = verf.adv_xtimestamp;
2337c478bd9Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2347c478bd9Sstevel@tonic-gate 		cryptbuf[1].key.high = cred->adc_fullname.window;
2357c478bd9Sstevel@tonic-gate 		cryptbuf[1].key.low = verf.adv_winverf;
2367c478bd9Sstevel@tonic-gate 		ivec.key.high = ivec.key.low = 0;
2377c478bd9Sstevel@tonic-gate 		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
2387c478bd9Sstevel@tonic-gate 		    2 * sizeof (des_block), DES_DECRYPT, (char *)&ivec);
2397c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2407c478bd9Sstevel@tonic-gate 		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
2417c478bd9Sstevel@tonic-gate 		    sizeof (des_block), DES_DECRYPT);
2427c478bd9Sstevel@tonic-gate 	}
2437c478bd9Sstevel@tonic-gate 	if (DES_FAILED(status)) {
2447c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: decryption failure\n");
2457c478bd9Sstevel@tonic-gate 		if (nick) {
2467c478bd9Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	 * XDR the decrypted timestamp
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
2557c478bd9Sstevel@tonic-gate 	timestamp.tv_sec = IXDR_GET_INT32(ixdr);
2567c478bd9Sstevel@tonic-gate 	timestamp.tv_usec = IXDR_GET_INT32(ixdr);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * Check for valid credentials and verifiers.
2607c478bd9Sstevel@tonic-gate 	 * They could be invalid because the key was flushed
2617c478bd9Sstevel@tonic-gate 	 * out of the cache, and so a new session should begin.
2627c478bd9Sstevel@tonic-gate 	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
2637c478bd9Sstevel@tonic-gate 	 */
2647c478bd9Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2657c478bd9Sstevel@tonic-gate 		window = IXDR_GET_U_INT32(ixdr);
2667c478bd9Sstevel@tonic-gate 		winverf = IXDR_GET_U_INT32(ixdr);
2677c478bd9Sstevel@tonic-gate 		if (winverf != window - 1) {
2687c478bd9Sstevel@tonic-gate 			RPCLOG(1, "_svcauth_des: window verifier mismatch %d\n",
2697c478bd9Sstevel@tonic-gate 				winverf);
2707c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* garbled credential */
2717c478bd9Sstevel@tonic-gate 		}
2727c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2737c478bd9Sstevel@tonic-gate 		window = nick_entry->window;
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	if (timestamp.tv_usec >= USEC_PER_SEC) {
2777c478bd9Sstevel@tonic-gate 		RPCLOG(1, "_svcauth_des: invalid usecs %ld\n",
2787c478bd9Sstevel@tonic-gate 					timestamp.tv_usec);
2797c478bd9Sstevel@tonic-gate 		/* cached out (bad key), or garbled verifier */
2807c478bd9Sstevel@tonic-gate 		if (nick) {
2817c478bd9Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	gethrestime(&now);
2877c478bd9Sstevel@tonic-gate 	current_time.tv_sec = now.tv_sec;
2887c478bd9Sstevel@tonic-gate 	current_time.tv_usec = now.tv_nsec / 1000;
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	current_time.tv_sec -= window;	/* allow for expiration */
2917c478bd9Sstevel@tonic-gate 	if (!BEFORE(&current_time, &timestamp)) {
2927c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: timestamp expired\n");
2937c478bd9Sstevel@tonic-gate 		/* replay, or garbled credential */
2947c478bd9Sstevel@tonic-gate 		if (nick) {
2957c478bd9Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2967c478bd9Sstevel@tonic-gate 		}
2977c478bd9Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
2987c478bd9Sstevel@tonic-gate 	}
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	/*
3017c478bd9Sstevel@tonic-gate 	 * xdr the timestamp before encrypting
3027c478bd9Sstevel@tonic-gate 	 */
3037c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
3047c478bd9Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
3057c478bd9Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	/*
3087c478bd9Sstevel@tonic-gate 	 * encrypt the timestamp
3097c478bd9Sstevel@tonic-gate 	 */
3107c478bd9Sstevel@tonic-gate 	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
3117c478bd9Sstevel@tonic-gate 	    sizeof (des_block), DES_ENCRYPT);
3127c478bd9Sstevel@tonic-gate 	if (DES_FAILED(status)) {
3137c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: encryption failure\n");
3147c478bd9Sstevel@tonic-gate 		if (nick) {
3157c478bd9Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
3167c478bd9Sstevel@tonic-gate 		}
3177c478bd9Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp = cryptbuf[0];
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	/*
3227c478bd9Sstevel@tonic-gate 	 * If a ADN_FULLNAME, create a new nickname cache entry.
3237c478bd9Sstevel@tonic-gate 	 */
3247c478bd9Sstevel@tonic-gate 	if (!nick) {
3257c478bd9Sstevel@tonic-gate 	    mutex_enter(&authdes_lock);
3267c478bd9Sstevel@tonic-gate 	    if (!(nick_entry = authdes_cache_new(cred->adc_fullname.name,
3277c478bd9Sstevel@tonic-gate 					sessionkey, window))) {
3287c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: can not create new cache entry\n");
3297c478bd9Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
3307c478bd9Sstevel@tonic-gate 		return (AUTH_FAILED);
3317c478bd9Sstevel@tonic-gate 	    }
3327c478bd9Sstevel@tonic-gate 	    mutex_enter(&nick_entry->lock);
3337c478bd9Sstevel@tonic-gate 	    mutex_exit(&authdes_lock);
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate 	verf.adv_nickname = nick_entry->nickname;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	/*
3387c478bd9Sstevel@tonic-gate 	 * Serialize the reply verifier, and update rqst
3397c478bd9Sstevel@tonic-gate 	 */
3407c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
3417c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
3427c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
3437c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
3447c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_int_u;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
3477c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
3487c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length =
3497c478bd9Sstevel@tonic-gate 	    (uint_t)((char *)ixdr - msg->rm_call.cb_verf.oa_base);
3507c478bd9Sstevel@tonic-gate 	if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
3517c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: invalid oa length\n");
3527c478bd9Sstevel@tonic-gate 		mutex_exit(&nick_entry->lock);
3537c478bd9Sstevel@tonic-gate 		return (AUTH_BADVERF);
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	/*
3577c478bd9Sstevel@tonic-gate 	 * We succeeded and finish cooking the credential.
3587c478bd9Sstevel@tonic-gate 	 * nicknames are cooked into fullnames
3597c478bd9Sstevel@tonic-gate 	 */
3607c478bd9Sstevel@tonic-gate 	if (!nick) {
3617c478bd9Sstevel@tonic-gate 		cred->adc_nickname = nick_entry->nickname;
3627c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = window;
3637c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
3647c478bd9Sstevel@tonic-gate 		cred->adc_namekind = ADN_FULLNAME;
3657c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name = nick_entry->rname;
3667c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key = nick_entry->key;
3677c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = nick_entry->window;
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 	mutex_exit(&nick_entry->lock);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	/*
3727c478bd9Sstevel@tonic-gate 	 * For every authdes_sweep_interval, delete cache entries that have been
3737c478bd9Sstevel@tonic-gate 	 * idled for authdes_cache_time.
3747c478bd9Sstevel@tonic-gate 	 */
3757c478bd9Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
3767c478bd9Sstevel@tonic-gate 	if ((gethrestime_sec() - authdes_last_swept) > authdes_sweep_interval)
3777c478bd9Sstevel@tonic-gate 		sweep_cache();
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	return (AUTH_OK);	/* we made it! */
3827c478bd9Sstevel@tonic-gate }
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate /*
3857c478bd9Sstevel@tonic-gate  * Initialization upon loading the rpcsec module.
3867c478bd9Sstevel@tonic-gate  */
3877c478bd9Sstevel@tonic-gate void
svcauthdes_init(void)3887c478bd9Sstevel@tonic-gate svcauthdes_init(void)
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	mutex_init(&authdes_lock, NULL, MUTEX_DEFAULT, NULL);
3917c478bd9Sstevel@tonic-gate 	/*
3927c478bd9Sstevel@tonic-gate 	 * Allocate des cache handle
3937c478bd9Sstevel@tonic-gate 	 */
3947c478bd9Sstevel@tonic-gate 	authdes_cache_handle = kmem_cache_create("authdes_cache_handle",
3957c478bd9Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry), 0, NULL, NULL,
3967c478bd9Sstevel@tonic-gate 			authdes_cache_reclaim, NULL, NULL, 0);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate /*
4007c478bd9Sstevel@tonic-gate  * Final actions upon exiting the rpcsec module.
4017c478bd9Sstevel@tonic-gate  */
4027c478bd9Sstevel@tonic-gate void
svcauthdes_fini(void)4037c478bd9Sstevel@tonic-gate svcauthdes_fini(void)
4047c478bd9Sstevel@tonic-gate {
4057c478bd9Sstevel@tonic-gate 	mutex_destroy(&authdes_lock);
4067c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(authdes_cache_handle);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate /*
4107c478bd9Sstevel@tonic-gate  * Local credential handling stuff.
4117c478bd9Sstevel@tonic-gate  * NOTE: bsd unix dependent.
4127c478bd9Sstevel@tonic-gate  * Other operating systems should put something else here.
4137c478bd9Sstevel@tonic-gate  */
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate struct bsdcred {
4167c478bd9Sstevel@tonic-gate 	uid_t uid;		/* cached uid */
4177c478bd9Sstevel@tonic-gate 	gid_t gid;		/* cached gid */
4187c478bd9Sstevel@tonic-gate 	short valid;		/* valid creds */
4197c478bd9Sstevel@tonic-gate 	short grouplen;	/* length of cached groups */
42067dbe2beSCasper H.S. Dik 	gid_t groups[1];	/* cached groups - allocate ngroups_max */
4217c478bd9Sstevel@tonic-gate };
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate /*
4247c478bd9Sstevel@tonic-gate  * Map a des credential into a unix cred.
4257c478bd9Sstevel@tonic-gate  * We cache the credential here so the application does
4267c478bd9Sstevel@tonic-gate  * not have to make an rpc call every time to interpret
4277c478bd9Sstevel@tonic-gate  * the credential.
4287c478bd9Sstevel@tonic-gate  */
4297c478bd9Sstevel@tonic-gate int
kauthdes_getucred(const struct authdes_cred * adc,cred_t * cr)4307c478bd9Sstevel@tonic-gate kauthdes_getucred(const struct authdes_cred *adc, cred_t *cr)
4317c478bd9Sstevel@tonic-gate {
4327c478bd9Sstevel@tonic-gate 	uid_t i_uid;
4337c478bd9Sstevel@tonic-gate 	gid_t i_gid;
4347c478bd9Sstevel@tonic-gate 	int i_grouplen;
4357c478bd9Sstevel@tonic-gate 	struct bsdcred *cred;
4367c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *nickentry;
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
4397c478bd9Sstevel@tonic-gate 	if (!(nickentry = authdes_cache_get(adc->adc_nickname))) {
4407c478bd9Sstevel@tonic-gate 		RPCLOG0(1, "authdes_getucred:  invalid nickname\n");
4417c478bd9Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
4427c478bd9Sstevel@tonic-gate 		return (0);
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	mutex_enter(&nickentry->lock);
4467c478bd9Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
4477c478bd9Sstevel@tonic-gate 	/* LINTED pointer alignment */
4487c478bd9Sstevel@tonic-gate 	cred = (struct bsdcred *)nickentry->localcred;
4497c478bd9Sstevel@tonic-gate 	if (!cred->valid) {
4507c478bd9Sstevel@tonic-gate 		/*
4517c478bd9Sstevel@tonic-gate 		 * not in cache: lookup
4527c478bd9Sstevel@tonic-gate 		 */
4537c478bd9Sstevel@tonic-gate 		if (netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
4547c478bd9Sstevel@tonic-gate 		    &i_grouplen, &cred->groups[0]) != RPC_SUCCESS) {
4557c478bd9Sstevel@tonic-gate 			/*
4567c478bd9Sstevel@tonic-gate 			 * Probably a host principal, since at this
4577c478bd9Sstevel@tonic-gate 			 * point we have valid keys. Note that later
4587c478bd9Sstevel@tonic-gate 			 * if the principal is not in the root list
4597c478bd9Sstevel@tonic-gate 			 * for NFS, we will be mapped to that exported
4607c478bd9Sstevel@tonic-gate 			 * file system's anonymous user, typically
4617c478bd9Sstevel@tonic-gate 			 * NOBODY. keyserv KEY_GETCRED will fail for a
4627c478bd9Sstevel@tonic-gate 			 * root-netnames so we assume root here.
4637c478bd9Sstevel@tonic-gate 			 * Currently NFS is the only caller of this
4647c478bd9Sstevel@tonic-gate 			 * routine. If other RPC services call this
4657c478bd9Sstevel@tonic-gate 			 * routine, it is up to that service to
4667c478bd9Sstevel@tonic-gate 			 * differentiate between local and remote
4677c478bd9Sstevel@tonic-gate 			 * roots.
4687c478bd9Sstevel@tonic-gate 			 */
4697c478bd9Sstevel@tonic-gate 			i_uid = 0;
4707c478bd9Sstevel@tonic-gate 			i_gid = 0;
4717c478bd9Sstevel@tonic-gate 			i_grouplen = 0;
4727c478bd9Sstevel@tonic-gate 		}
4737c478bd9Sstevel@tonic-gate 		RPCLOG0(2, "authdes_getucred:  missed ucred cache\n");
4747c478bd9Sstevel@tonic-gate 		cred->uid = i_uid;
4757c478bd9Sstevel@tonic-gate 		cred->gid = i_gid;
4767c478bd9Sstevel@tonic-gate 		cred->grouplen = (short)i_grouplen;
4777c478bd9Sstevel@tonic-gate 		cred->valid = 1;
4787c478bd9Sstevel@tonic-gate 	}
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	/*
4817c478bd9Sstevel@tonic-gate 	 * cached credentials
4827c478bd9Sstevel@tonic-gate 	 */
4837c478bd9Sstevel@tonic-gate 	if (crsetugid(cr, cred->uid, cred->gid) != 0 ||
4847c478bd9Sstevel@tonic-gate 	    crsetgroups(cr, cred->grouplen, &cred->groups[0]) != 0) {
4857c478bd9Sstevel@tonic-gate 		mutex_exit(&nickentry->lock);
4867c478bd9Sstevel@tonic-gate 		return (0);
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 	mutex_exit(&nickentry->lock);
4897c478bd9Sstevel@tonic-gate 	return (1);
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate /*
4937c478bd9Sstevel@tonic-gate  * Create a new cache_entry and put it in authdes_cache table.
4947c478bd9Sstevel@tonic-gate  * Caller should have already locked the authdes_cache table.
4957c478bd9Sstevel@tonic-gate  */
4967c478bd9Sstevel@tonic-gate struct authdes_cache_entry *
authdes_cache_new(char * fullname,des_block * sessionkey,uint32_t window)4977c478bd9Sstevel@tonic-gate authdes_cache_new(char *fullname, des_block *sessionkey, uint32_t window) {
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *new, *head;
5007c478bd9Sstevel@tonic-gate 	struct bsdcred *ucred;
5017c478bd9Sstevel@tonic-gate 	int index;
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	if (!(new = kmem_cache_alloc(authdes_cache_handle, KM_SLEEP))) {
5047c478bd9Sstevel@tonic-gate 		return (NULL);
5057c478bd9Sstevel@tonic-gate 	}
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	if (!(new->rname = kmem_alloc(strlen(fullname) + 1, KM_NOSLEEP))) {
5087c478bd9Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
5097c478bd9Sstevel@tonic-gate 		return (NULL);
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 
51267dbe2beSCasper H.S. Dik 	if (!(ucred = kmem_alloc(sizeof (struct bsdcred) +
51367dbe2beSCasper H.S. Dik 	    (ngroups_max - 1) * sizeof (gid_t), KM_NOSLEEP))) {
5147c478bd9Sstevel@tonic-gate 		kmem_free(new->rname, strlen(fullname) + 1);
5157c478bd9Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
5167c478bd9Sstevel@tonic-gate 		return (NULL);
5177c478bd9Sstevel@tonic-gate 	}
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	(void) strcpy(new->rname, fullname);
5207c478bd9Sstevel@tonic-gate 	ucred->valid = 0;
5217c478bd9Sstevel@tonic-gate 	new->localcred = (caddr_t)ucred;
5227c478bd9Sstevel@tonic-gate 	new->key = *sessionkey;
5237c478bd9Sstevel@tonic-gate 	new->window = window;
5247c478bd9Sstevel@tonic-gate 	new->ref_time = gethrestime_sec();
5257c478bd9Sstevel@tonic-gate 	new->nickname = Nickname++;
5267c478bd9Sstevel@tonic-gate 	mutex_init(&new->lock, NULL, MUTEX_DEFAULT, NULL);
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	/* put new into the hash table */
5297c478bd9Sstevel@tonic-gate 	index = HASH(new->nickname);
5307c478bd9Sstevel@tonic-gate 	head = authdes_cache[index];
5317c478bd9Sstevel@tonic-gate 	if ((new->next = head) != NULL) {
5327c478bd9Sstevel@tonic-gate 		head->prev = new;
5337c478bd9Sstevel@tonic-gate 	}
5347c478bd9Sstevel@tonic-gate 	authdes_cache[index] = new;
5357c478bd9Sstevel@tonic-gate 	new->prev = NULL;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/* update the LRU list */
5387c478bd9Sstevel@tonic-gate 	new->lru_prev = NULL;
5397c478bd9Sstevel@tonic-gate 	if ((new->lru_next = lru_first) != NULL) {
5407c478bd9Sstevel@tonic-gate 		lru_first->lru_prev = new;
5417c478bd9Sstevel@tonic-gate 	} else {
5427c478bd9Sstevel@tonic-gate 		lru_last = new;
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 	lru_first = new;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	authdes_ncache++;
5477c478bd9Sstevel@tonic-gate 	return (new);
5487c478bd9Sstevel@tonic-gate }
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate /*
5517c478bd9Sstevel@tonic-gate  * Get an existing cache entry from authdes_cache table.
5527c478bd9Sstevel@tonic-gate  * The caller should have locked the authdes_cache table.
5537c478bd9Sstevel@tonic-gate  */
5547c478bd9Sstevel@tonic-gate struct authdes_cache_entry *
authdes_cache_get(uint32_t nickname)5557c478bd9Sstevel@tonic-gate authdes_cache_get(uint32_t nickname) {
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *cur = NULL;
5587c478bd9Sstevel@tonic-gate 	int index = HASH(nickname);
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
5617c478bd9Sstevel@tonic-gate 	for (cur = authdes_cache[index]; cur; cur = cur->next) {
5627c478bd9Sstevel@tonic-gate 		if ((cur->nickname == nickname)) {
5637c478bd9Sstevel@tonic-gate 			/* find it, update the LRU list */
5647c478bd9Sstevel@tonic-gate 			if (cur != lru_first) {
5657c478bd9Sstevel@tonic-gate 			    cur->lru_prev->lru_next = cur->lru_next;
5667c478bd9Sstevel@tonic-gate 			    if (cur->lru_next != NULL) {
5677c478bd9Sstevel@tonic-gate 				cur->lru_next->lru_prev = cur->lru_prev;
5687c478bd9Sstevel@tonic-gate 			    } else {
5697c478bd9Sstevel@tonic-gate 				lru_last = cur->lru_prev;
5707c478bd9Sstevel@tonic-gate 			    }
5717c478bd9Sstevel@tonic-gate 			    cur->lru_prev = NULL;
5727c478bd9Sstevel@tonic-gate 			    cur->lru_next = lru_first;
5737c478bd9Sstevel@tonic-gate 			    lru_first->lru_prev = cur;
5747c478bd9Sstevel@tonic-gate 			    lru_first = cur;
5757c478bd9Sstevel@tonic-gate 			}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 			cur->ref_time = gethrestime_sec();
5787c478bd9Sstevel@tonic-gate 			authdes_ncachehits++;
5797c478bd9Sstevel@tonic-gate 			return (cur);
5807c478bd9Sstevel@tonic-gate 		}
5817c478bd9Sstevel@tonic-gate 	}
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	authdes_ncachemisses++;
5847c478bd9Sstevel@tonic-gate 	return (NULL);
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate  * authdes_cache_reclaim() is called by the kernel memory allocator
5897c478bd9Sstevel@tonic-gate  * when memory is low. This routine will reclaim 25% of the least recent
5907c478bd9Sstevel@tonic-gate  * used cache entries above the low water mark (low_cache_entries).
5917c478bd9Sstevel@tonic-gate  * If the cache entries have already hit the low water mark, it will
5927c478bd9Sstevel@tonic-gate  * return 1 cache entry.
5937c478bd9Sstevel@tonic-gate  */
5947c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5957c478bd9Sstevel@tonic-gate void
authdes_cache_reclaim(void * pdata)5967c478bd9Sstevel@tonic-gate authdes_cache_reclaim(void *pdata) {
5977c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *p;
5987c478bd9Sstevel@tonic-gate 	int n, i;
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
6017c478bd9Sstevel@tonic-gate 	n = authdes_ncache - low_cache_entries;
6027c478bd9Sstevel@tonic-gate 	n = n > 0 ? n/4 : 1;
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6057c478bd9Sstevel@tonic-gate 		if ((p = lru_last) == lru_first)
6067c478bd9Sstevel@tonic-gate 			break;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 		/* Update the hash linked list */
6097c478bd9Sstevel@tonic-gate 		if (p->prev == NULL) {
6107c478bd9Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
6117c478bd9Sstevel@tonic-gate 		} else {
6127c478bd9Sstevel@tonic-gate 			p->prev->next = p->next;
6137c478bd9Sstevel@tonic-gate 		}
6147c478bd9Sstevel@tonic-gate 		if (p->next != NULL) {
6157c478bd9Sstevel@tonic-gate 			p->next->prev = p->prev;
6167c478bd9Sstevel@tonic-gate 		}
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		/* update the LRU linked list */
6197c478bd9Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
6207c478bd9Sstevel@tonic-gate 		lru_last = p->lru_prev;
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
62367dbe2beSCasper H.S. Dik 		kmem_free(p->localcred, sizeof (struct bsdcred) +
62467dbe2beSCasper H.S. Dik 		    (ngroups_max - 1) * sizeof (gid_t));
6257c478bd9Sstevel@tonic-gate 		mutex_destroy(&p->lock);
6267c478bd9Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 		authdes_ncache--;
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
6317c478bd9Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n",
6327c478bd9Sstevel@tonic-gate 				authdes_ncache);
6337c478bd9Sstevel@tonic-gate }
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate /*
6367c478bd9Sstevel@tonic-gate  *  Walk through the LRU doubly-linked list and delete the cache
6377c478bd9Sstevel@tonic-gate  *  entries that have not been used for more than authdes_cache_time.
6387c478bd9Sstevel@tonic-gate  *
6397c478bd9Sstevel@tonic-gate  *  Caller should have locked the cache table.
6407c478bd9Sstevel@tonic-gate  */
6417c478bd9Sstevel@tonic-gate void
sweep_cache()6427c478bd9Sstevel@tonic-gate sweep_cache() {
6437c478bd9Sstevel@tonic-gate 	struct authdes_cache_entry *p;
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
6467c478bd9Sstevel@tonic-gate 	while ((p = lru_last) != lru_first) {
6477c478bd9Sstevel@tonic-gate 		IS_ALIGNED(p);
6487c478bd9Sstevel@tonic-gate 		NOT_DEAD(p);
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 		/*
6517c478bd9Sstevel@tonic-gate 		 * If the last LRU entry idled less than authdes_cache_time,
6527c478bd9Sstevel@tonic-gate 		 * we are done with the sweeping.
6537c478bd9Sstevel@tonic-gate 		 */
6547c478bd9Sstevel@tonic-gate 		if (p->ref_time + authdes_cache_time > gethrestime_sec())
6557c478bd9Sstevel@tonic-gate 			break;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 		/* update the hash linked list */
6587c478bd9Sstevel@tonic-gate 		if (p->prev == NULL) {
6597c478bd9Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
6607c478bd9Sstevel@tonic-gate 		} else {
6617c478bd9Sstevel@tonic-gate 			p->prev->next = p->next;
6627c478bd9Sstevel@tonic-gate 		}
6637c478bd9Sstevel@tonic-gate 		if (p->next != NULL) {
6647c478bd9Sstevel@tonic-gate 			p->next->prev = p->prev;
6657c478bd9Sstevel@tonic-gate 		}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 		/* update the LRU linked list */
6687c478bd9Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
6697c478bd9Sstevel@tonic-gate 		lru_last = p->lru_prev;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
67267dbe2beSCasper H.S. Dik 		kmem_free(p->localcred, sizeof (struct bsdcred) +
67367dbe2beSCasper H.S. Dik 		    (ngroups_max - 1) * sizeof (gid_t));
6747c478bd9Sstevel@tonic-gate 		mutex_destroy(&p->lock);
6757c478bd9Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 		authdes_ncache--;
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	authdes_last_swept = gethrestime_sec();
6817c478bd9Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n",
6827c478bd9Sstevel@tonic-gate 				authdes_ncache);
6837c478bd9Sstevel@tonic-gate }
684