10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*4321Scasper  * Common Development and Distribution License (the "License").
6*4321Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*4321Scasper  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
310Sstevel@tonic-gate  * under license from the Regents of the University of California.
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
350Sstevel@tonic-gate 
360Sstevel@tonic-gate /*
370Sstevel@tonic-gate  * svcauth_des.c, server-side des authentication
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * We insure for the service the following:
400Sstevel@tonic-gate  * (1) The timestamp microseconds do not exceed 1 million.
410Sstevel@tonic-gate  * (2) The timestamp plus the window is less than the current time.
420Sstevel@tonic-gate  * (3) The timestamp is not less than the one previously
430Sstevel@tonic-gate  *	seen in the current session.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * It is up to the server to determine if the window size is
460Sstevel@tonic-gate  * too small.
470Sstevel@tonic-gate  */
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <sys/types.h>
500Sstevel@tonic-gate #include <sys/time.h>
510Sstevel@tonic-gate #include <sys/systm.h>
520Sstevel@tonic-gate #include <sys/param.h>
530Sstevel@tonic-gate #include <sys/stream.h>
540Sstevel@tonic-gate #include <sys/stropts.h>
550Sstevel@tonic-gate #include <sys/strsubr.h>
560Sstevel@tonic-gate #include <sys/tiuser.h>
570Sstevel@tonic-gate #include <sys/tihdr.h>
580Sstevel@tonic-gate #include <sys/t_kuser.h>
590Sstevel@tonic-gate #include <sys/t_lock.h>
600Sstevel@tonic-gate #include <sys/debug.h>
610Sstevel@tonic-gate #include <sys/kmem.h>
620Sstevel@tonic-gate #include <sys/time.h>
630Sstevel@tonic-gate #include <sys/cmn_err.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #include <rpc/types.h>
660Sstevel@tonic-gate #include <rpc/xdr.h>
670Sstevel@tonic-gate #include <rpc/auth.h>
680Sstevel@tonic-gate #include <rpc/auth_des.h>
690Sstevel@tonic-gate #include <rpc/rpc_msg.h>
700Sstevel@tonic-gate #include <rpc/svc.h>
710Sstevel@tonic-gate #include <rpc/svc_auth.h>
720Sstevel@tonic-gate #include <rpc/clnt.h>
730Sstevel@tonic-gate #include <rpc/des_crypt.h>
740Sstevel@tonic-gate 
750Sstevel@tonic-gate #define	USEC_PER_SEC 1000000
760Sstevel@tonic-gate #define	BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */)
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /*
790Sstevel@tonic-gate  * Cache of conversation keys and some other useful items.
800Sstevel@tonic-gate  * The hash table size is controled via authdes_cachesz variable.
810Sstevel@tonic-gate  * The authdes_cachesz has to be the power of 2.
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate #define	AUTHDES_CACHE_TABLE_SZ 1024
840Sstevel@tonic-gate static int authdes_cachesz = AUTHDES_CACHE_TABLE_SZ;
850Sstevel@tonic-gate #define	HASH(key) ((key) & (authdes_cachesz - 1))
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* low water mark for the number of cache entries */
880Sstevel@tonic-gate static int low_cache_entries = 128;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate struct authdes_cache_entry {
910Sstevel@tonic-gate 	uint32_t nickname;		/* nick name id */
920Sstevel@tonic-gate 	uint32_t window;		/* credential lifetime window */
930Sstevel@tonic-gate 	des_block key;			/* conversation key */
940Sstevel@tonic-gate 	time_t	ref_time;		/* time referenced previously */
950Sstevel@tonic-gate 	char *rname;			/* client's name */
960Sstevel@tonic-gate 	caddr_t localcred;		/* generic local credential */
970Sstevel@tonic-gate 	struct authdes_cache_entry *prev, *next;  /* hash table linked list */
980Sstevel@tonic-gate 	struct authdes_cache_entry *lru_prev, *lru_next; /* LRU linked list */
990Sstevel@tonic-gate 	kmutex_t lock;			/* cache entry lock */
1000Sstevel@tonic-gate };
1010Sstevel@tonic-gate static struct authdes_cache_entry **authdes_cache; /* [authdes_cachesz] */
1020Sstevel@tonic-gate static struct authdes_cache_entry *lru_first = NULL;
1030Sstevel@tonic-gate static struct authdes_cache_entry *lru_last = NULL;
1040Sstevel@tonic-gate static kmutex_t authdes_lock;		/* cache table lock */
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate static struct kmem_cache *authdes_cache_handle;
1070Sstevel@tonic-gate static uint32_t	Nickname = 0;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_new(char *,
1100Sstevel@tonic-gate 					des_block *, uint32_t);
1110Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_get(uint32_t);
1120Sstevel@tonic-gate static void authdes_cache_reclaim(void *);
1130Sstevel@tonic-gate static void sweep_cache();
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate /*
1160Sstevel@tonic-gate  * After 12 hours, check and delete cache entries that have been
1170Sstevel@tonic-gate  * idled for more than 10 hours.
1180Sstevel@tonic-gate  */
1190Sstevel@tonic-gate static time_t authdes_sweep_interval = 12*60*60;
1200Sstevel@tonic-gate static time_t authdes_cache_time = 10*60*60;
1210Sstevel@tonic-gate static time_t authdes_last_swept = 0;
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate /*
1240Sstevel@tonic-gate  * cache statistics
1250Sstevel@tonic-gate  */
1260Sstevel@tonic-gate static int authdes_ncache = 0; /* number of current cached entries */
1270Sstevel@tonic-gate static int authdes_ncachehits = 0; /* #times cache hit */
1280Sstevel@tonic-gate static int authdes_ncachemisses = 0; /* #times cache missed */
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate #define	NOT_DEAD(ptr)   ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
1310Sstevel@tonic-gate #define	IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0)
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate  * Service side authenticator for AUTH_DES
1350Sstevel@tonic-gate  */
1360Sstevel@tonic-gate enum auth_stat
1370Sstevel@tonic-gate _svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
1380Sstevel@tonic-gate {
1390Sstevel@tonic-gate 	int32_t *ixdr;
1400Sstevel@tonic-gate 	des_block cryptbuf[2];
1410Sstevel@tonic-gate 	struct authdes_cred *cred;
1420Sstevel@tonic-gate 	struct authdes_verf verf;
1430Sstevel@tonic-gate 	int status;
1440Sstevel@tonic-gate 	des_block *sessionkey;
1450Sstevel@tonic-gate 	des_block ivec;
1460Sstevel@tonic-gate 	uint32_t window, winverf, namelen;
1470Sstevel@tonic-gate 	bool_t nick;
1480Sstevel@tonic-gate 	struct timeval timestamp, current_time;
1490Sstevel@tonic-gate 	struct authdes_cache_entry *nick_entry;
1500Sstevel@tonic-gate 	struct area {
1510Sstevel@tonic-gate 		struct authdes_cred area_cred;
1520Sstevel@tonic-gate 		char area_netname[MAXNETNAMELEN+1];
1530Sstevel@tonic-gate 	} *area;
1540Sstevel@tonic-gate 	timestruc_t now;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
1570Sstevel@tonic-gate 	if (authdes_cache == NULL) {
1580Sstevel@tonic-gate 		authdes_cache = kmem_zalloc(
1590Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry *) * authdes_cachesz,
1600Sstevel@tonic-gate 			KM_SLEEP);
1610Sstevel@tonic-gate 	}
1620Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	/* LINTED pointer alignment */
1650Sstevel@tonic-gate 	area = (struct area *)rqst->rq_clntcred;
1660Sstevel@tonic-gate 	cred = (struct authdes_cred *)&area->area_cred;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	/*
1690Sstevel@tonic-gate 	 * Get the credential
1700Sstevel@tonic-gate 	 */
1710Sstevel@tonic-gate 	/* LINTED pointer alignment */
1720Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
1730Sstevel@tonic-gate 	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
1740Sstevel@tonic-gate 	switch (cred->adc_namekind) {
1750Sstevel@tonic-gate 	case ADN_FULLNAME:
1760Sstevel@tonic-gate 		namelen = IXDR_GET_U_INT32(ixdr);
1770Sstevel@tonic-gate 		if (namelen > MAXNETNAMELEN)
1780Sstevel@tonic-gate 			return (AUTH_BADCRED);
1790Sstevel@tonic-gate 		cred->adc_fullname.name = area->area_netname;
1800Sstevel@tonic-gate 		bcopy(ixdr, cred->adc_fullname.name, namelen);
1810Sstevel@tonic-gate 		cred->adc_fullname.name[namelen] = 0;
1820Sstevel@tonic-gate 		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
1830Sstevel@tonic-gate 		cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
1840Sstevel@tonic-gate 		cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
1850Sstevel@tonic-gate 		cred->adc_fullname.window = (uint32_t)*ixdr++;
1860Sstevel@tonic-gate 		nick = FALSE;
1870Sstevel@tonic-gate 		break;
1880Sstevel@tonic-gate 	case ADN_NICKNAME:
1890Sstevel@tonic-gate 		cred->adc_nickname = (uint32_t)*ixdr++;
1900Sstevel@tonic-gate 		nick = TRUE;
1910Sstevel@tonic-gate 		break;
1920Sstevel@tonic-gate 	default:
1930Sstevel@tonic-gate 		return (AUTH_BADCRED);
1940Sstevel@tonic-gate 	}
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	/*
1970Sstevel@tonic-gate 	 * Get the verifier
1980Sstevel@tonic-gate 	 */
1990Sstevel@tonic-gate 	/* LINTED pointer alignment */
2000Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
2010Sstevel@tonic-gate 	verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
2020Sstevel@tonic-gate 	verf.adv_xtimestamp.key.low =  (uint32_t)*ixdr++;
2030Sstevel@tonic-gate 	verf.adv_int_u = (uint32_t)*ixdr++;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	/*
2070Sstevel@tonic-gate 	 * Get the conversation key
2080Sstevel@tonic-gate 	 */
2090Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2100Sstevel@tonic-gate 		sessionkey = &cred->adc_fullname.key;
2110Sstevel@tonic-gate 		if (key_decryptsession(cred->adc_fullname.name, sessionkey) !=
2120Sstevel@tonic-gate 		    RPC_SUCCESS) {
2130Sstevel@tonic-gate 		    return (AUTH_BADCRED); /* key not found */
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2160Sstevel@tonic-gate 		mutex_enter(&authdes_lock);
2170Sstevel@tonic-gate 		if (!(nick_entry = authdes_cache_get(cred->adc_nickname))) {
2180Sstevel@tonic-gate 		    RPCLOG(1, "_svcauth_des: nickname %d not in the cache\n",
2190Sstevel@tonic-gate 						cred->adc_nickname);
2200Sstevel@tonic-gate 		    mutex_exit(&authdes_lock);
2210Sstevel@tonic-gate 		    return (AUTH_BADCRED);	/* need refresh */
2220Sstevel@tonic-gate 		}
2230Sstevel@tonic-gate 		sessionkey = &nick_entry->key;
2240Sstevel@tonic-gate 		mutex_enter(&nick_entry->lock);
2250Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
2260Sstevel@tonic-gate 	}
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/*
2290Sstevel@tonic-gate 	 * Decrypt the timestamp
2300Sstevel@tonic-gate 	 */
2310Sstevel@tonic-gate 	cryptbuf[0] = verf.adv_xtimestamp;
2320Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2330Sstevel@tonic-gate 		cryptbuf[1].key.high = cred->adc_fullname.window;
2340Sstevel@tonic-gate 		cryptbuf[1].key.low = verf.adv_winverf;
2350Sstevel@tonic-gate 		ivec.key.high = ivec.key.low = 0;
2360Sstevel@tonic-gate 		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
2370Sstevel@tonic-gate 		    2 * sizeof (des_block), DES_DECRYPT, (char *)&ivec);
2380Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2390Sstevel@tonic-gate 		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
2400Sstevel@tonic-gate 		    sizeof (des_block), DES_DECRYPT);
2410Sstevel@tonic-gate 	}
2420Sstevel@tonic-gate 	if (DES_FAILED(status)) {
2430Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: decryption failure\n");
2440Sstevel@tonic-gate 		if (nick) {
2450Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2460Sstevel@tonic-gate 		}
2470Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	/*
2510Sstevel@tonic-gate 	 * XDR the decrypted timestamp
2520Sstevel@tonic-gate 	 */
2530Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
2540Sstevel@tonic-gate 	timestamp.tv_sec = IXDR_GET_INT32(ixdr);
2550Sstevel@tonic-gate 	timestamp.tv_usec = IXDR_GET_INT32(ixdr);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	/*
2580Sstevel@tonic-gate 	 * Check for valid credentials and verifiers.
2590Sstevel@tonic-gate 	 * They could be invalid because the key was flushed
2600Sstevel@tonic-gate 	 * out of the cache, and so a new session should begin.
2610Sstevel@tonic-gate 	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
2620Sstevel@tonic-gate 	 */
2630Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
2640Sstevel@tonic-gate 		window = IXDR_GET_U_INT32(ixdr);
2650Sstevel@tonic-gate 		winverf = IXDR_GET_U_INT32(ixdr);
2660Sstevel@tonic-gate 		if (winverf != window - 1) {
2670Sstevel@tonic-gate 			RPCLOG(1, "_svcauth_des: window verifier mismatch %d\n",
2680Sstevel@tonic-gate 				winverf);
2690Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* garbled credential */
2700Sstevel@tonic-gate 		}
2710Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2720Sstevel@tonic-gate 		window = nick_entry->window;
2730Sstevel@tonic-gate 	}
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	if (timestamp.tv_usec >= USEC_PER_SEC) {
2760Sstevel@tonic-gate 		RPCLOG(1, "_svcauth_des: invalid usecs %ld\n",
2770Sstevel@tonic-gate 					timestamp.tv_usec);
2780Sstevel@tonic-gate 		/* cached out (bad key), or garbled verifier */
2790Sstevel@tonic-gate 		if (nick) {
2800Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
2830Sstevel@tonic-gate 	}
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	gethrestime(&now);
2860Sstevel@tonic-gate 	current_time.tv_sec = now.tv_sec;
2870Sstevel@tonic-gate 	current_time.tv_usec = now.tv_nsec / 1000;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	current_time.tv_sec -= window;	/* allow for expiration */
2900Sstevel@tonic-gate 	if (!BEFORE(&current_time, &timestamp)) {
2910Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: timestamp expired\n");
2920Sstevel@tonic-gate 		/* replay, or garbled credential */
2930Sstevel@tonic-gate 		if (nick) {
2940Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
2950Sstevel@tonic-gate 		}
2960Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
2970Sstevel@tonic-gate 	}
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	/*
3000Sstevel@tonic-gate 	 * xdr the timestamp before encrypting
3010Sstevel@tonic-gate 	 */
3020Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
3030Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
3040Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	/*
3070Sstevel@tonic-gate 	 * encrypt the timestamp
3080Sstevel@tonic-gate 	 */
3090Sstevel@tonic-gate 	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
3100Sstevel@tonic-gate 	    sizeof (des_block), DES_ENCRYPT);
3110Sstevel@tonic-gate 	if (DES_FAILED(status)) {
3120Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: encryption failure\n");
3130Sstevel@tonic-gate 		if (nick) {
3140Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
3150Sstevel@tonic-gate 		}
3160Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 	verf.adv_xtimestamp = cryptbuf[0];
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	/*
3210Sstevel@tonic-gate 	 * If a ADN_FULLNAME, create a new nickname cache entry.
3220Sstevel@tonic-gate 	 */
3230Sstevel@tonic-gate 	if (!nick) {
3240Sstevel@tonic-gate 	    mutex_enter(&authdes_lock);
3250Sstevel@tonic-gate 	    if (!(nick_entry = authdes_cache_new(cred->adc_fullname.name,
3260Sstevel@tonic-gate 					sessionkey, window))) {
3270Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: can not create new cache entry\n");
3280Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
3290Sstevel@tonic-gate 		return (AUTH_FAILED);
3300Sstevel@tonic-gate 	    }
3310Sstevel@tonic-gate 	    mutex_enter(&nick_entry->lock);
3320Sstevel@tonic-gate 	    mutex_exit(&authdes_lock);
3330Sstevel@tonic-gate 	}
3340Sstevel@tonic-gate 	verf.adv_nickname = nick_entry->nickname;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	/*
3370Sstevel@tonic-gate 	 * Serialize the reply verifier, and update rqst
3380Sstevel@tonic-gate 	 */
3390Sstevel@tonic-gate 	/* LINTED pointer alignment */
3400Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
3410Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
3420Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
3430Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_int_u;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
3460Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
3470Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length =
3480Sstevel@tonic-gate 	    (uint_t)((char *)ixdr - msg->rm_call.cb_verf.oa_base);
3490Sstevel@tonic-gate 	if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
3500Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: invalid oa length\n");
3510Sstevel@tonic-gate 		mutex_exit(&nick_entry->lock);
3520Sstevel@tonic-gate 		return (AUTH_BADVERF);
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	/*
3560Sstevel@tonic-gate 	 * We succeeded and finish cooking the credential.
3570Sstevel@tonic-gate 	 * nicknames are cooked into fullnames
3580Sstevel@tonic-gate 	 */
3590Sstevel@tonic-gate 	if (!nick) {
3600Sstevel@tonic-gate 		cred->adc_nickname = nick_entry->nickname;
3610Sstevel@tonic-gate 		cred->adc_fullname.window = window;
3620Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
3630Sstevel@tonic-gate 		cred->adc_namekind = ADN_FULLNAME;
3640Sstevel@tonic-gate 		cred->adc_fullname.name = nick_entry->rname;
3650Sstevel@tonic-gate 		cred->adc_fullname.key = nick_entry->key;
3660Sstevel@tonic-gate 		cred->adc_fullname.window = nick_entry->window;
3670Sstevel@tonic-gate 	}
3680Sstevel@tonic-gate 	mutex_exit(&nick_entry->lock);
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	/*
3710Sstevel@tonic-gate 	 * For every authdes_sweep_interval, delete cache entries that have been
3720Sstevel@tonic-gate 	 * idled for authdes_cache_time.
3730Sstevel@tonic-gate 	 */
3740Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
3750Sstevel@tonic-gate 	if ((gethrestime_sec() - authdes_last_swept) > authdes_sweep_interval)
3760Sstevel@tonic-gate 		sweep_cache();
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	return (AUTH_OK);	/* we made it! */
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate /*
3840Sstevel@tonic-gate  * Initialization upon loading the rpcsec module.
3850Sstevel@tonic-gate  */
3860Sstevel@tonic-gate void
3870Sstevel@tonic-gate svcauthdes_init(void)
3880Sstevel@tonic-gate {
3890Sstevel@tonic-gate 	mutex_init(&authdes_lock, NULL, MUTEX_DEFAULT, NULL);
3900Sstevel@tonic-gate 	/*
3910Sstevel@tonic-gate 	 * Allocate des cache handle
3920Sstevel@tonic-gate 	 */
3930Sstevel@tonic-gate 	authdes_cache_handle = kmem_cache_create("authdes_cache_handle",
3940Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry), 0, NULL, NULL,
3950Sstevel@tonic-gate 			authdes_cache_reclaim, NULL, NULL, 0);
3960Sstevel@tonic-gate }
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate /*
3990Sstevel@tonic-gate  * Final actions upon exiting the rpcsec module.
4000Sstevel@tonic-gate  */
4010Sstevel@tonic-gate void
4020Sstevel@tonic-gate svcauthdes_fini(void)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	mutex_destroy(&authdes_lock);
4050Sstevel@tonic-gate 	kmem_cache_destroy(authdes_cache_handle);
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate  * Local credential handling stuff.
4100Sstevel@tonic-gate  * NOTE: bsd unix dependent.
4110Sstevel@tonic-gate  * Other operating systems should put something else here.
4120Sstevel@tonic-gate  */
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate struct bsdcred {
4150Sstevel@tonic-gate 	uid_t uid;		/* cached uid */
4160Sstevel@tonic-gate 	gid_t gid;		/* cached gid */
4170Sstevel@tonic-gate 	short valid;		/* valid creds */
4180Sstevel@tonic-gate 	short grouplen;	/* length of cached groups */
419*4321Scasper 	gid_t groups[NGROUPS_UMAX];	/* cached groups */
4200Sstevel@tonic-gate };
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate /*
4230Sstevel@tonic-gate  * Map a des credential into a unix cred.
4240Sstevel@tonic-gate  * We cache the credential here so the application does
4250Sstevel@tonic-gate  * not have to make an rpc call every time to interpret
4260Sstevel@tonic-gate  * the credential.
4270Sstevel@tonic-gate  */
4280Sstevel@tonic-gate int
4290Sstevel@tonic-gate kauthdes_getucred(const struct authdes_cred *adc, cred_t *cr)
4300Sstevel@tonic-gate {
4310Sstevel@tonic-gate 	uid_t i_uid;
4320Sstevel@tonic-gate 	gid_t i_gid;
4330Sstevel@tonic-gate 	int i_grouplen;
4340Sstevel@tonic-gate 	struct bsdcred *cred;
4350Sstevel@tonic-gate 	struct authdes_cache_entry *nickentry;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
4380Sstevel@tonic-gate 	if (!(nickentry = authdes_cache_get(adc->adc_nickname))) {
4390Sstevel@tonic-gate 		RPCLOG0(1, "authdes_getucred:  invalid nickname\n");
4400Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
4410Sstevel@tonic-gate 		return (0);
4420Sstevel@tonic-gate 	}
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	mutex_enter(&nickentry->lock);
4450Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
4460Sstevel@tonic-gate 	/* LINTED pointer alignment */
4470Sstevel@tonic-gate 	cred = (struct bsdcred *)nickentry->localcred;
4480Sstevel@tonic-gate 	if (!cred->valid) {
4490Sstevel@tonic-gate 		/*
4500Sstevel@tonic-gate 		 * not in cache: lookup
4510Sstevel@tonic-gate 		 */
4520Sstevel@tonic-gate 		if (netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
4530Sstevel@tonic-gate 		    &i_grouplen, &cred->groups[0]) != RPC_SUCCESS) {
4540Sstevel@tonic-gate 			/*
4550Sstevel@tonic-gate 			 * Probably a host principal, since at this
4560Sstevel@tonic-gate 			 * point we have valid keys. Note that later
4570Sstevel@tonic-gate 			 * if the principal is not in the root list
4580Sstevel@tonic-gate 			 * for NFS, we will be mapped to that exported
4590Sstevel@tonic-gate 			 * file system's anonymous user, typically
4600Sstevel@tonic-gate 			 * NOBODY. keyserv KEY_GETCRED will fail for a
4610Sstevel@tonic-gate 			 * root-netnames so we assume root here.
4620Sstevel@tonic-gate 			 * Currently NFS is the only caller of this
4630Sstevel@tonic-gate 			 * routine. If other RPC services call this
4640Sstevel@tonic-gate 			 * routine, it is up to that service to
4650Sstevel@tonic-gate 			 * differentiate between local and remote
4660Sstevel@tonic-gate 			 * roots.
4670Sstevel@tonic-gate 			 */
4680Sstevel@tonic-gate 			i_uid = 0;
4690Sstevel@tonic-gate 			i_gid = 0;
4700Sstevel@tonic-gate 			i_grouplen = 0;
4710Sstevel@tonic-gate 		}
4720Sstevel@tonic-gate 		RPCLOG0(2, "authdes_getucred:  missed ucred cache\n");
4730Sstevel@tonic-gate 		cred->uid = i_uid;
4740Sstevel@tonic-gate 		cred->gid = i_gid;
4750Sstevel@tonic-gate 		cred->grouplen = (short)i_grouplen;
4760Sstevel@tonic-gate 		cred->valid = 1;
4770Sstevel@tonic-gate 	}
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	/*
4800Sstevel@tonic-gate 	 * cached credentials
4810Sstevel@tonic-gate 	 */
4820Sstevel@tonic-gate 	if (crsetugid(cr, cred->uid, cred->gid) != 0 ||
4830Sstevel@tonic-gate 	    crsetgroups(cr, cred->grouplen, &cred->groups[0]) != 0) {
4840Sstevel@tonic-gate 		mutex_exit(&nickentry->lock);
4850Sstevel@tonic-gate 		return (0);
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 	mutex_exit(&nickentry->lock);
4880Sstevel@tonic-gate 	return (1);
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate /*
4920Sstevel@tonic-gate  * Create a new cache_entry and put it in authdes_cache table.
4930Sstevel@tonic-gate  * Caller should have already locked the authdes_cache table.
4940Sstevel@tonic-gate  */
4950Sstevel@tonic-gate struct authdes_cache_entry *
4960Sstevel@tonic-gate authdes_cache_new(char *fullname, des_block *sessionkey, uint32_t window) {
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	struct authdes_cache_entry *new, *head;
4990Sstevel@tonic-gate 	struct bsdcred *ucred;
5000Sstevel@tonic-gate 	int index;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	if (!(new = kmem_cache_alloc(authdes_cache_handle, KM_SLEEP))) {
5030Sstevel@tonic-gate 		return (NULL);
5040Sstevel@tonic-gate 	}
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	if (!(new->rname = kmem_alloc(strlen(fullname) + 1, KM_NOSLEEP))) {
5070Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
5080Sstevel@tonic-gate 		return (NULL);
5090Sstevel@tonic-gate 	}
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	if (!(ucred = (struct bsdcred *)kmem_alloc(sizeof (struct bsdcred),
5120Sstevel@tonic-gate 			KM_NOSLEEP))) {
5130Sstevel@tonic-gate 		kmem_free(new->rname, strlen(fullname) + 1);
5140Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
5150Sstevel@tonic-gate 		return (NULL);
5160Sstevel@tonic-gate 	}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	(void) strcpy(new->rname, fullname);
5190Sstevel@tonic-gate 	ucred->valid = 0;
5200Sstevel@tonic-gate 	new->localcred = (caddr_t)ucred;
5210Sstevel@tonic-gate 	new->key = *sessionkey;
5220Sstevel@tonic-gate 	new->window = window;
5230Sstevel@tonic-gate 	new->ref_time = gethrestime_sec();
5240Sstevel@tonic-gate 	new->nickname = Nickname++;
5250Sstevel@tonic-gate 	mutex_init(&new->lock, NULL, MUTEX_DEFAULT, NULL);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	/* put new into the hash table */
5280Sstevel@tonic-gate 	index = HASH(new->nickname);
5290Sstevel@tonic-gate 	head = authdes_cache[index];
5300Sstevel@tonic-gate 	if ((new->next = head) != NULL) {
5310Sstevel@tonic-gate 		head->prev = new;
5320Sstevel@tonic-gate 	}
5330Sstevel@tonic-gate 	authdes_cache[index] = new;
5340Sstevel@tonic-gate 	new->prev = NULL;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	/* update the LRU list */
5370Sstevel@tonic-gate 	new->lru_prev = NULL;
5380Sstevel@tonic-gate 	if ((new->lru_next = lru_first) != NULL) {
5390Sstevel@tonic-gate 		lru_first->lru_prev = new;
5400Sstevel@tonic-gate 	} else {
5410Sstevel@tonic-gate 		lru_last = new;
5420Sstevel@tonic-gate 	}
5430Sstevel@tonic-gate 	lru_first = new;
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	authdes_ncache++;
5460Sstevel@tonic-gate 	return (new);
5470Sstevel@tonic-gate }
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate /*
5500Sstevel@tonic-gate  * Get an existing cache entry from authdes_cache table.
5510Sstevel@tonic-gate  * The caller should have locked the authdes_cache table.
5520Sstevel@tonic-gate  */
5530Sstevel@tonic-gate struct authdes_cache_entry *
5540Sstevel@tonic-gate authdes_cache_get(uint32_t nickname) {
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	struct authdes_cache_entry *cur = NULL;
5570Sstevel@tonic-gate 	int index = HASH(nickname);
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
5600Sstevel@tonic-gate 	for (cur = authdes_cache[index]; cur; cur = cur->next) {
5610Sstevel@tonic-gate 		if ((cur->nickname == nickname)) {
5620Sstevel@tonic-gate 			/* find it, update the LRU list */
5630Sstevel@tonic-gate 			if (cur != lru_first) {
5640Sstevel@tonic-gate 			    cur->lru_prev->lru_next = cur->lru_next;
5650Sstevel@tonic-gate 			    if (cur->lru_next != NULL) {
5660Sstevel@tonic-gate 				cur->lru_next->lru_prev = cur->lru_prev;
5670Sstevel@tonic-gate 			    } else {
5680Sstevel@tonic-gate 				lru_last = cur->lru_prev;
5690Sstevel@tonic-gate 			    }
5700Sstevel@tonic-gate 			    cur->lru_prev = NULL;
5710Sstevel@tonic-gate 			    cur->lru_next = lru_first;
5720Sstevel@tonic-gate 			    lru_first->lru_prev = cur;
5730Sstevel@tonic-gate 			    lru_first = cur;
5740Sstevel@tonic-gate 			}
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 			cur->ref_time = gethrestime_sec();
5770Sstevel@tonic-gate 			authdes_ncachehits++;
5780Sstevel@tonic-gate 			return (cur);
5790Sstevel@tonic-gate 		}
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	authdes_ncachemisses++;
5830Sstevel@tonic-gate 	return (NULL);
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate /*
5870Sstevel@tonic-gate  * authdes_cache_reclaim() is called by the kernel memory allocator
5880Sstevel@tonic-gate  * when memory is low. This routine will reclaim 25% of the least recent
5890Sstevel@tonic-gate  * used cache entries above the low water mark (low_cache_entries).
5900Sstevel@tonic-gate  * If the cache entries have already hit the low water mark, it will
5910Sstevel@tonic-gate  * return 1 cache entry.
5920Sstevel@tonic-gate  */
5930Sstevel@tonic-gate /*ARGSUSED*/
5940Sstevel@tonic-gate void
5950Sstevel@tonic-gate authdes_cache_reclaim(void *pdata) {
5960Sstevel@tonic-gate 	struct authdes_cache_entry *p;
5970Sstevel@tonic-gate 	int n, i;
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
6000Sstevel@tonic-gate 	n = authdes_ncache - low_cache_entries;
6010Sstevel@tonic-gate 	n = n > 0 ? n/4 : 1;
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6040Sstevel@tonic-gate 		if ((p = lru_last) == lru_first)
6050Sstevel@tonic-gate 			break;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 		/* Update the hash linked list */
6080Sstevel@tonic-gate 		if (p->prev == NULL) {
6090Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
6100Sstevel@tonic-gate 		} else {
6110Sstevel@tonic-gate 			p->prev->next = p->next;
6120Sstevel@tonic-gate 		}
6130Sstevel@tonic-gate 		if (p->next != NULL) {
6140Sstevel@tonic-gate 			p->next->prev = p->prev;
6150Sstevel@tonic-gate 		}
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 		/* update the LRU linked list */
6180Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
6190Sstevel@tonic-gate 		lru_last = p->lru_prev;
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
6220Sstevel@tonic-gate 		kmem_free(p->localcred, sizeof (struct bsdcred));
6230Sstevel@tonic-gate 		mutex_destroy(&p->lock);
6240Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		authdes_ncache--;
6270Sstevel@tonic-gate 	}
6280Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
6290Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n",
6300Sstevel@tonic-gate 				authdes_ncache);
6310Sstevel@tonic-gate }
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate /*
6340Sstevel@tonic-gate  *  Walk through the LRU doubly-linked list and delete the cache
6350Sstevel@tonic-gate  *  entries that have not been used for more than authdes_cache_time.
6360Sstevel@tonic-gate  *
6370Sstevel@tonic-gate  *  Caller should have locked the cache table.
6380Sstevel@tonic-gate  */
6390Sstevel@tonic-gate void
6400Sstevel@tonic-gate sweep_cache() {
6410Sstevel@tonic-gate 	struct authdes_cache_entry *p;
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
6440Sstevel@tonic-gate 	while ((p = lru_last) != lru_first) {
6450Sstevel@tonic-gate 		IS_ALIGNED(p);
6460Sstevel@tonic-gate 		NOT_DEAD(p);
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 		/*
6490Sstevel@tonic-gate 		 * If the last LRU entry idled less than authdes_cache_time,
6500Sstevel@tonic-gate 		 * we are done with the sweeping.
6510Sstevel@tonic-gate 		 */
6520Sstevel@tonic-gate 		if (p->ref_time + authdes_cache_time > gethrestime_sec())
6530Sstevel@tonic-gate 			break;
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 		/* update the hash linked list */
6560Sstevel@tonic-gate 		if (p->prev == NULL) {
6570Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
6580Sstevel@tonic-gate 		} else {
6590Sstevel@tonic-gate 			p->prev->next = p->next;
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 		if (p->next != NULL) {
6620Sstevel@tonic-gate 			p->next->prev = p->prev;
6630Sstevel@tonic-gate 		}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		/* update the LRU linked list */
6660Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
6670Sstevel@tonic-gate 		lru_last = p->lru_prev;
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
6700Sstevel@tonic-gate 		kmem_free(p->localcred, sizeof (struct bsdcred));
6710Sstevel@tonic-gate 		mutex_destroy(&p->lock);
6720Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		authdes_ncache--;
6750Sstevel@tonic-gate 	}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	authdes_last_swept = gethrestime_sec();
6780Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n",
6790Sstevel@tonic-gate 				authdes_ncache);
6800Sstevel@tonic-gate }
681