1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate /*
31*0Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley 4.3 BSD
32*0Sstevel@tonic-gate  * under license from the Regents of the University of California.
33*0Sstevel@tonic-gate  */
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate /*
38*0Sstevel@tonic-gate  * svcauth_des.c, server-side des authentication
39*0Sstevel@tonic-gate  *
40*0Sstevel@tonic-gate  * We insure for the service the following:
41*0Sstevel@tonic-gate  * (1) The timestamp microseconds do not exceed 1 million.
42*0Sstevel@tonic-gate  * (2) The timestamp plus the window is less than the current time.
43*0Sstevel@tonic-gate  * (3) The timestamp is not less than the one previously
44*0Sstevel@tonic-gate  *	seen in the current session.
45*0Sstevel@tonic-gate  *
46*0Sstevel@tonic-gate  * It is up to the server to determine if the window size is
47*0Sstevel@tonic-gate  * too small.
48*0Sstevel@tonic-gate  */
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate #include <sys/types.h>
51*0Sstevel@tonic-gate #include <sys/time.h>
52*0Sstevel@tonic-gate #include <sys/systm.h>
53*0Sstevel@tonic-gate #include <sys/param.h>
54*0Sstevel@tonic-gate #include <sys/stream.h>
55*0Sstevel@tonic-gate #include <sys/stropts.h>
56*0Sstevel@tonic-gate #include <sys/strsubr.h>
57*0Sstevel@tonic-gate #include <sys/tiuser.h>
58*0Sstevel@tonic-gate #include <sys/tihdr.h>
59*0Sstevel@tonic-gate #include <sys/t_kuser.h>
60*0Sstevel@tonic-gate #include <sys/t_lock.h>
61*0Sstevel@tonic-gate #include <sys/debug.h>
62*0Sstevel@tonic-gate #include <sys/kmem.h>
63*0Sstevel@tonic-gate #include <sys/time.h>
64*0Sstevel@tonic-gate #include <sys/cmn_err.h>
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate #include <rpc/types.h>
67*0Sstevel@tonic-gate #include <rpc/xdr.h>
68*0Sstevel@tonic-gate #include <rpc/auth.h>
69*0Sstevel@tonic-gate #include <rpc/auth_des.h>
70*0Sstevel@tonic-gate #include <rpc/rpc_msg.h>
71*0Sstevel@tonic-gate #include <rpc/svc.h>
72*0Sstevel@tonic-gate #include <rpc/svc_auth.h>
73*0Sstevel@tonic-gate #include <rpc/clnt.h>
74*0Sstevel@tonic-gate #include <rpc/des_crypt.h>
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate #define	USEC_PER_SEC 1000000
77*0Sstevel@tonic-gate #define	BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */)
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate  * Cache of conversation keys and some other useful items.
81*0Sstevel@tonic-gate  * The hash table size is controled via authdes_cachesz variable.
82*0Sstevel@tonic-gate  * The authdes_cachesz has to be the power of 2.
83*0Sstevel@tonic-gate  */
84*0Sstevel@tonic-gate #define	AUTHDES_CACHE_TABLE_SZ 1024
85*0Sstevel@tonic-gate static int authdes_cachesz = AUTHDES_CACHE_TABLE_SZ;
86*0Sstevel@tonic-gate #define	HASH(key) ((key) & (authdes_cachesz - 1))
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate /* low water mark for the number of cache entries */
89*0Sstevel@tonic-gate static int low_cache_entries = 128;
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate struct authdes_cache_entry {
92*0Sstevel@tonic-gate 	uint32_t nickname;		/* nick name id */
93*0Sstevel@tonic-gate 	uint32_t window;		/* credential lifetime window */
94*0Sstevel@tonic-gate 	des_block key;			/* conversation key */
95*0Sstevel@tonic-gate 	time_t	ref_time;		/* time referenced previously */
96*0Sstevel@tonic-gate 	char *rname;			/* client's name */
97*0Sstevel@tonic-gate 	caddr_t localcred;		/* generic local credential */
98*0Sstevel@tonic-gate 	struct authdes_cache_entry *prev, *next;  /* hash table linked list */
99*0Sstevel@tonic-gate 	struct authdes_cache_entry *lru_prev, *lru_next; /* LRU linked list */
100*0Sstevel@tonic-gate 	kmutex_t lock;			/* cache entry lock */
101*0Sstevel@tonic-gate };
102*0Sstevel@tonic-gate static struct authdes_cache_entry **authdes_cache; /* [authdes_cachesz] */
103*0Sstevel@tonic-gate static struct authdes_cache_entry *lru_first = NULL;
104*0Sstevel@tonic-gate static struct authdes_cache_entry *lru_last = NULL;
105*0Sstevel@tonic-gate static kmutex_t authdes_lock;		/* cache table lock */
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate static struct kmem_cache *authdes_cache_handle;
108*0Sstevel@tonic-gate static uint32_t	Nickname = 0;
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_new(char *,
111*0Sstevel@tonic-gate 					des_block *, uint32_t);
112*0Sstevel@tonic-gate static struct authdes_cache_entry *authdes_cache_get(uint32_t);
113*0Sstevel@tonic-gate static void authdes_cache_reclaim(void *);
114*0Sstevel@tonic-gate static void sweep_cache();
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate /*
117*0Sstevel@tonic-gate  * After 12 hours, check and delete cache entries that have been
118*0Sstevel@tonic-gate  * idled for more than 10 hours.
119*0Sstevel@tonic-gate  */
120*0Sstevel@tonic-gate static time_t authdes_sweep_interval = 12*60*60;
121*0Sstevel@tonic-gate static time_t authdes_cache_time = 10*60*60;
122*0Sstevel@tonic-gate static time_t authdes_last_swept = 0;
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate /*
125*0Sstevel@tonic-gate  * cache statistics
126*0Sstevel@tonic-gate  */
127*0Sstevel@tonic-gate static int authdes_ncache = 0; /* number of current cached entries */
128*0Sstevel@tonic-gate static int authdes_ncachehits = 0; /* #times cache hit */
129*0Sstevel@tonic-gate static int authdes_ncachemisses = 0; /* #times cache missed */
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate #define	NOT_DEAD(ptr)   ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
132*0Sstevel@tonic-gate #define	IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0)
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate /*
135*0Sstevel@tonic-gate  * Service side authenticator for AUTH_DES
136*0Sstevel@tonic-gate  */
137*0Sstevel@tonic-gate enum auth_stat
138*0Sstevel@tonic-gate _svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
139*0Sstevel@tonic-gate {
140*0Sstevel@tonic-gate 	int32_t *ixdr;
141*0Sstevel@tonic-gate 	des_block cryptbuf[2];
142*0Sstevel@tonic-gate 	struct authdes_cred *cred;
143*0Sstevel@tonic-gate 	struct authdes_verf verf;
144*0Sstevel@tonic-gate 	int status;
145*0Sstevel@tonic-gate 	des_block *sessionkey;
146*0Sstevel@tonic-gate 	des_block ivec;
147*0Sstevel@tonic-gate 	uint32_t window, winverf, namelen;
148*0Sstevel@tonic-gate 	bool_t nick;
149*0Sstevel@tonic-gate 	struct timeval timestamp, current_time;
150*0Sstevel@tonic-gate 	struct authdes_cache_entry *nick_entry;
151*0Sstevel@tonic-gate 	struct area {
152*0Sstevel@tonic-gate 		struct authdes_cred area_cred;
153*0Sstevel@tonic-gate 		char area_netname[MAXNETNAMELEN+1];
154*0Sstevel@tonic-gate 	} *area;
155*0Sstevel@tonic-gate 	timestruc_t now;
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
158*0Sstevel@tonic-gate 	if (authdes_cache == NULL) {
159*0Sstevel@tonic-gate 		authdes_cache = kmem_zalloc(
160*0Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry *) * authdes_cachesz,
161*0Sstevel@tonic-gate 			KM_SLEEP);
162*0Sstevel@tonic-gate 	}
163*0Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
166*0Sstevel@tonic-gate 	area = (struct area *)rqst->rq_clntcred;
167*0Sstevel@tonic-gate 	cred = (struct authdes_cred *)&area->area_cred;
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	/*
170*0Sstevel@tonic-gate 	 * Get the credential
171*0Sstevel@tonic-gate 	 */
172*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
173*0Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
174*0Sstevel@tonic-gate 	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
175*0Sstevel@tonic-gate 	switch (cred->adc_namekind) {
176*0Sstevel@tonic-gate 	case ADN_FULLNAME:
177*0Sstevel@tonic-gate 		namelen = IXDR_GET_U_INT32(ixdr);
178*0Sstevel@tonic-gate 		if (namelen > MAXNETNAMELEN)
179*0Sstevel@tonic-gate 			return (AUTH_BADCRED);
180*0Sstevel@tonic-gate 		cred->adc_fullname.name = area->area_netname;
181*0Sstevel@tonic-gate 		bcopy(ixdr, cred->adc_fullname.name, namelen);
182*0Sstevel@tonic-gate 		cred->adc_fullname.name[namelen] = 0;
183*0Sstevel@tonic-gate 		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
184*0Sstevel@tonic-gate 		cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
185*0Sstevel@tonic-gate 		cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
186*0Sstevel@tonic-gate 		cred->adc_fullname.window = (uint32_t)*ixdr++;
187*0Sstevel@tonic-gate 		nick = FALSE;
188*0Sstevel@tonic-gate 		break;
189*0Sstevel@tonic-gate 	case ADN_NICKNAME:
190*0Sstevel@tonic-gate 		cred->adc_nickname = (uint32_t)*ixdr++;
191*0Sstevel@tonic-gate 		nick = TRUE;
192*0Sstevel@tonic-gate 		break;
193*0Sstevel@tonic-gate 	default:
194*0Sstevel@tonic-gate 		return (AUTH_BADCRED);
195*0Sstevel@tonic-gate 	}
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 	/*
198*0Sstevel@tonic-gate 	 * Get the verifier
199*0Sstevel@tonic-gate 	 */
200*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
201*0Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
202*0Sstevel@tonic-gate 	verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
203*0Sstevel@tonic-gate 	verf.adv_xtimestamp.key.low =  (uint32_t)*ixdr++;
204*0Sstevel@tonic-gate 	verf.adv_int_u = (uint32_t)*ixdr++;
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	/*
208*0Sstevel@tonic-gate 	 * Get the conversation key
209*0Sstevel@tonic-gate 	 */
210*0Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
211*0Sstevel@tonic-gate 		sessionkey = &cred->adc_fullname.key;
212*0Sstevel@tonic-gate 		if (key_decryptsession(cred->adc_fullname.name, sessionkey) !=
213*0Sstevel@tonic-gate 		    RPC_SUCCESS) {
214*0Sstevel@tonic-gate 		    return (AUTH_BADCRED); /* key not found */
215*0Sstevel@tonic-gate 		}
216*0Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
217*0Sstevel@tonic-gate 		mutex_enter(&authdes_lock);
218*0Sstevel@tonic-gate 		if (!(nick_entry = authdes_cache_get(cred->adc_nickname))) {
219*0Sstevel@tonic-gate 		    RPCLOG(1, "_svcauth_des: nickname %d not in the cache\n",
220*0Sstevel@tonic-gate 						cred->adc_nickname);
221*0Sstevel@tonic-gate 		    mutex_exit(&authdes_lock);
222*0Sstevel@tonic-gate 		    return (AUTH_BADCRED);	/* need refresh */
223*0Sstevel@tonic-gate 		}
224*0Sstevel@tonic-gate 		sessionkey = &nick_entry->key;
225*0Sstevel@tonic-gate 		mutex_enter(&nick_entry->lock);
226*0Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
227*0Sstevel@tonic-gate 	}
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate 	/*
230*0Sstevel@tonic-gate 	 * Decrypt the timestamp
231*0Sstevel@tonic-gate 	 */
232*0Sstevel@tonic-gate 	cryptbuf[0] = verf.adv_xtimestamp;
233*0Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
234*0Sstevel@tonic-gate 		cryptbuf[1].key.high = cred->adc_fullname.window;
235*0Sstevel@tonic-gate 		cryptbuf[1].key.low = verf.adv_winverf;
236*0Sstevel@tonic-gate 		ivec.key.high = ivec.key.low = 0;
237*0Sstevel@tonic-gate 		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
238*0Sstevel@tonic-gate 		    2 * sizeof (des_block), DES_DECRYPT, (char *)&ivec);
239*0Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
240*0Sstevel@tonic-gate 		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
241*0Sstevel@tonic-gate 		    sizeof (des_block), DES_DECRYPT);
242*0Sstevel@tonic-gate 	}
243*0Sstevel@tonic-gate 	if (DES_FAILED(status)) {
244*0Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: decryption failure\n");
245*0Sstevel@tonic-gate 		if (nick) {
246*0Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
247*0Sstevel@tonic-gate 		}
248*0Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
249*0Sstevel@tonic-gate 	}
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 	/*
252*0Sstevel@tonic-gate 	 * XDR the decrypted timestamp
253*0Sstevel@tonic-gate 	 */
254*0Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
255*0Sstevel@tonic-gate 	timestamp.tv_sec = IXDR_GET_INT32(ixdr);
256*0Sstevel@tonic-gate 	timestamp.tv_usec = IXDR_GET_INT32(ixdr);
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	/*
259*0Sstevel@tonic-gate 	 * Check for valid credentials and verifiers.
260*0Sstevel@tonic-gate 	 * They could be invalid because the key was flushed
261*0Sstevel@tonic-gate 	 * out of the cache, and so a new session should begin.
262*0Sstevel@tonic-gate 	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
263*0Sstevel@tonic-gate 	 */
264*0Sstevel@tonic-gate 	if (!nick) { /* ADN_FULLNAME */
265*0Sstevel@tonic-gate 		window = IXDR_GET_U_INT32(ixdr);
266*0Sstevel@tonic-gate 		winverf = IXDR_GET_U_INT32(ixdr);
267*0Sstevel@tonic-gate 		if (winverf != window - 1) {
268*0Sstevel@tonic-gate 			RPCLOG(1, "_svcauth_des: window verifier mismatch %d\n",
269*0Sstevel@tonic-gate 				winverf);
270*0Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* garbled credential */
271*0Sstevel@tonic-gate 		}
272*0Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
273*0Sstevel@tonic-gate 		window = nick_entry->window;
274*0Sstevel@tonic-gate 	}
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	if (timestamp.tv_usec >= USEC_PER_SEC) {
277*0Sstevel@tonic-gate 		RPCLOG(1, "_svcauth_des: invalid usecs %ld\n",
278*0Sstevel@tonic-gate 					timestamp.tv_usec);
279*0Sstevel@tonic-gate 		/* cached out (bad key), or garbled verifier */
280*0Sstevel@tonic-gate 		if (nick) {
281*0Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
282*0Sstevel@tonic-gate 		}
283*0Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
284*0Sstevel@tonic-gate 	}
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 	gethrestime(&now);
287*0Sstevel@tonic-gate 	current_time.tv_sec = now.tv_sec;
288*0Sstevel@tonic-gate 	current_time.tv_usec = now.tv_nsec / 1000;
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	current_time.tv_sec -= window;	/* allow for expiration */
291*0Sstevel@tonic-gate 	if (!BEFORE(&current_time, &timestamp)) {
292*0Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: timestamp expired\n");
293*0Sstevel@tonic-gate 		/* replay, or garbled credential */
294*0Sstevel@tonic-gate 		if (nick) {
295*0Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
296*0Sstevel@tonic-gate 		}
297*0Sstevel@tonic-gate 		return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
298*0Sstevel@tonic-gate 	}
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	/*
301*0Sstevel@tonic-gate 	 * xdr the timestamp before encrypting
302*0Sstevel@tonic-gate 	 */
303*0Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
304*0Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
305*0Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	/*
308*0Sstevel@tonic-gate 	 * encrypt the timestamp
309*0Sstevel@tonic-gate 	 */
310*0Sstevel@tonic-gate 	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
311*0Sstevel@tonic-gate 	    sizeof (des_block), DES_ENCRYPT);
312*0Sstevel@tonic-gate 	if (DES_FAILED(status)) {
313*0Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: encryption failure\n");
314*0Sstevel@tonic-gate 		if (nick) {
315*0Sstevel@tonic-gate 			mutex_exit(&nick_entry->lock);
316*0Sstevel@tonic-gate 		}
317*0Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 	verf.adv_xtimestamp = cryptbuf[0];
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	/*
322*0Sstevel@tonic-gate 	 * If a ADN_FULLNAME, create a new nickname cache entry.
323*0Sstevel@tonic-gate 	 */
324*0Sstevel@tonic-gate 	if (!nick) {
325*0Sstevel@tonic-gate 	    mutex_enter(&authdes_lock);
326*0Sstevel@tonic-gate 	    if (!(nick_entry = authdes_cache_new(cred->adc_fullname.name,
327*0Sstevel@tonic-gate 					sessionkey, window))) {
328*0Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: can not create new cache entry\n");
329*0Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
330*0Sstevel@tonic-gate 		return (AUTH_FAILED);
331*0Sstevel@tonic-gate 	    }
332*0Sstevel@tonic-gate 	    mutex_enter(&nick_entry->lock);
333*0Sstevel@tonic-gate 	    mutex_exit(&authdes_lock);
334*0Sstevel@tonic-gate 	}
335*0Sstevel@tonic-gate 	verf.adv_nickname = nick_entry->nickname;
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 	/*
338*0Sstevel@tonic-gate 	 * Serialize the reply verifier, and update rqst
339*0Sstevel@tonic-gate 	 */
340*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
341*0Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
342*0Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
343*0Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
344*0Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_int_u;
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
347*0Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
348*0Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length =
349*0Sstevel@tonic-gate 	    (uint_t)((char *)ixdr - msg->rm_call.cb_verf.oa_base);
350*0Sstevel@tonic-gate 	if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
351*0Sstevel@tonic-gate 		RPCLOG0(1, "_svcauth_des: invalid oa length\n");
352*0Sstevel@tonic-gate 		mutex_exit(&nick_entry->lock);
353*0Sstevel@tonic-gate 		return (AUTH_BADVERF);
354*0Sstevel@tonic-gate 	}
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 	/*
357*0Sstevel@tonic-gate 	 * We succeeded and finish cooking the credential.
358*0Sstevel@tonic-gate 	 * nicknames are cooked into fullnames
359*0Sstevel@tonic-gate 	 */
360*0Sstevel@tonic-gate 	if (!nick) {
361*0Sstevel@tonic-gate 		cred->adc_nickname = nick_entry->nickname;
362*0Sstevel@tonic-gate 		cred->adc_fullname.window = window;
363*0Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
364*0Sstevel@tonic-gate 		cred->adc_namekind = ADN_FULLNAME;
365*0Sstevel@tonic-gate 		cred->adc_fullname.name = nick_entry->rname;
366*0Sstevel@tonic-gate 		cred->adc_fullname.key = nick_entry->key;
367*0Sstevel@tonic-gate 		cred->adc_fullname.window = nick_entry->window;
368*0Sstevel@tonic-gate 	}
369*0Sstevel@tonic-gate 	mutex_exit(&nick_entry->lock);
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	/*
372*0Sstevel@tonic-gate 	 * For every authdes_sweep_interval, delete cache entries that have been
373*0Sstevel@tonic-gate 	 * idled for authdes_cache_time.
374*0Sstevel@tonic-gate 	 */
375*0Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
376*0Sstevel@tonic-gate 	if ((gethrestime_sec() - authdes_last_swept) > authdes_sweep_interval)
377*0Sstevel@tonic-gate 		sweep_cache();
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate 	return (AUTH_OK);	/* we made it! */
382*0Sstevel@tonic-gate }
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate /*
385*0Sstevel@tonic-gate  * Initialization upon loading the rpcsec module.
386*0Sstevel@tonic-gate  */
387*0Sstevel@tonic-gate void
388*0Sstevel@tonic-gate svcauthdes_init(void)
389*0Sstevel@tonic-gate {
390*0Sstevel@tonic-gate 	mutex_init(&authdes_lock, NULL, MUTEX_DEFAULT, NULL);
391*0Sstevel@tonic-gate 	/*
392*0Sstevel@tonic-gate 	 * Allocate des cache handle
393*0Sstevel@tonic-gate 	 */
394*0Sstevel@tonic-gate 	authdes_cache_handle = kmem_cache_create("authdes_cache_handle",
395*0Sstevel@tonic-gate 			sizeof (struct authdes_cache_entry), 0, NULL, NULL,
396*0Sstevel@tonic-gate 			authdes_cache_reclaim, NULL, NULL, 0);
397*0Sstevel@tonic-gate }
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate /*
400*0Sstevel@tonic-gate  * Final actions upon exiting the rpcsec module.
401*0Sstevel@tonic-gate  */
402*0Sstevel@tonic-gate void
403*0Sstevel@tonic-gate svcauthdes_fini(void)
404*0Sstevel@tonic-gate {
405*0Sstevel@tonic-gate 	mutex_destroy(&authdes_lock);
406*0Sstevel@tonic-gate 	kmem_cache_destroy(authdes_cache_handle);
407*0Sstevel@tonic-gate }
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate /*
410*0Sstevel@tonic-gate  * Local credential handling stuff.
411*0Sstevel@tonic-gate  * NOTE: bsd unix dependent.
412*0Sstevel@tonic-gate  * Other operating systems should put something else here.
413*0Sstevel@tonic-gate  */
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate struct bsdcred {
416*0Sstevel@tonic-gate 	uid_t uid;		/* cached uid */
417*0Sstevel@tonic-gate 	gid_t gid;		/* cached gid */
418*0Sstevel@tonic-gate 	short valid;		/* valid creds */
419*0Sstevel@tonic-gate 	short grouplen;	/* length of cached groups */
420*0Sstevel@tonic-gate 	int groups[NGROUPS_UMAX];	/* cached groups */
421*0Sstevel@tonic-gate };
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate /*
424*0Sstevel@tonic-gate  * Map a des credential into a unix cred.
425*0Sstevel@tonic-gate  * We cache the credential here so the application does
426*0Sstevel@tonic-gate  * not have to make an rpc call every time to interpret
427*0Sstevel@tonic-gate  * the credential.
428*0Sstevel@tonic-gate  */
429*0Sstevel@tonic-gate int
430*0Sstevel@tonic-gate kauthdes_getucred(const struct authdes_cred *adc, cred_t *cr)
431*0Sstevel@tonic-gate {
432*0Sstevel@tonic-gate 	uid_t i_uid;
433*0Sstevel@tonic-gate 	gid_t i_gid;
434*0Sstevel@tonic-gate 	int i_grouplen;
435*0Sstevel@tonic-gate 	struct bsdcred *cred;
436*0Sstevel@tonic-gate 	struct authdes_cache_entry *nickentry;
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
439*0Sstevel@tonic-gate 	if (!(nickentry = authdes_cache_get(adc->adc_nickname))) {
440*0Sstevel@tonic-gate 		RPCLOG0(1, "authdes_getucred:  invalid nickname\n");
441*0Sstevel@tonic-gate 		mutex_exit(&authdes_lock);
442*0Sstevel@tonic-gate 		return (0);
443*0Sstevel@tonic-gate 	}
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	mutex_enter(&nickentry->lock);
446*0Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
447*0Sstevel@tonic-gate 	/* LINTED pointer alignment */
448*0Sstevel@tonic-gate 	cred = (struct bsdcred *)nickentry->localcred;
449*0Sstevel@tonic-gate 	if (!cred->valid) {
450*0Sstevel@tonic-gate 		/*
451*0Sstevel@tonic-gate 		 * not in cache: lookup
452*0Sstevel@tonic-gate 		 */
453*0Sstevel@tonic-gate 		if (netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
454*0Sstevel@tonic-gate 		    &i_grouplen, &cred->groups[0]) != RPC_SUCCESS) {
455*0Sstevel@tonic-gate 			/*
456*0Sstevel@tonic-gate 			 * Probably a host principal, since at this
457*0Sstevel@tonic-gate 			 * point we have valid keys. Note that later
458*0Sstevel@tonic-gate 			 * if the principal is not in the root list
459*0Sstevel@tonic-gate 			 * for NFS, we will be mapped to that exported
460*0Sstevel@tonic-gate 			 * file system's anonymous user, typically
461*0Sstevel@tonic-gate 			 * NOBODY. keyserv KEY_GETCRED will fail for a
462*0Sstevel@tonic-gate 			 * root-netnames so we assume root here.
463*0Sstevel@tonic-gate 			 * Currently NFS is the only caller of this
464*0Sstevel@tonic-gate 			 * routine. If other RPC services call this
465*0Sstevel@tonic-gate 			 * routine, it is up to that service to
466*0Sstevel@tonic-gate 			 * differentiate between local and remote
467*0Sstevel@tonic-gate 			 * roots.
468*0Sstevel@tonic-gate 			 */
469*0Sstevel@tonic-gate 			i_uid = 0;
470*0Sstevel@tonic-gate 			i_gid = 0;
471*0Sstevel@tonic-gate 			i_grouplen = 0;
472*0Sstevel@tonic-gate 		}
473*0Sstevel@tonic-gate 		RPCLOG0(2, "authdes_getucred:  missed ucred cache\n");
474*0Sstevel@tonic-gate 		cred->uid = i_uid;
475*0Sstevel@tonic-gate 		cred->gid = i_gid;
476*0Sstevel@tonic-gate 		cred->grouplen = (short)i_grouplen;
477*0Sstevel@tonic-gate 		cred->valid = 1;
478*0Sstevel@tonic-gate 	}
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate 	/*
481*0Sstevel@tonic-gate 	 * cached credentials
482*0Sstevel@tonic-gate 	 */
483*0Sstevel@tonic-gate 	if (crsetugid(cr, cred->uid, cred->gid) != 0 ||
484*0Sstevel@tonic-gate 	    crsetgroups(cr, cred->grouplen, &cred->groups[0]) != 0) {
485*0Sstevel@tonic-gate 		mutex_exit(&nickentry->lock);
486*0Sstevel@tonic-gate 		return (0);
487*0Sstevel@tonic-gate 	}
488*0Sstevel@tonic-gate 	mutex_exit(&nickentry->lock);
489*0Sstevel@tonic-gate 	return (1);
490*0Sstevel@tonic-gate }
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate /*
493*0Sstevel@tonic-gate  * Create a new cache_entry and put it in authdes_cache table.
494*0Sstevel@tonic-gate  * Caller should have already locked the authdes_cache table.
495*0Sstevel@tonic-gate  */
496*0Sstevel@tonic-gate struct authdes_cache_entry *
497*0Sstevel@tonic-gate authdes_cache_new(char *fullname, des_block *sessionkey, uint32_t window) {
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	struct authdes_cache_entry *new, *head;
500*0Sstevel@tonic-gate 	struct bsdcred *ucred;
501*0Sstevel@tonic-gate 	int index;
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 	if (!(new = kmem_cache_alloc(authdes_cache_handle, KM_SLEEP))) {
504*0Sstevel@tonic-gate 		return (NULL);
505*0Sstevel@tonic-gate 	}
506*0Sstevel@tonic-gate 
507*0Sstevel@tonic-gate 	if (!(new->rname = kmem_alloc(strlen(fullname) + 1, KM_NOSLEEP))) {
508*0Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
509*0Sstevel@tonic-gate 		return (NULL);
510*0Sstevel@tonic-gate 	}
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 	if (!(ucred = (struct bsdcred *)kmem_alloc(sizeof (struct bsdcred),
513*0Sstevel@tonic-gate 			KM_NOSLEEP))) {
514*0Sstevel@tonic-gate 		kmem_free(new->rname, strlen(fullname) + 1);
515*0Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, new);
516*0Sstevel@tonic-gate 		return (NULL);
517*0Sstevel@tonic-gate 	}
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 	(void) strcpy(new->rname, fullname);
520*0Sstevel@tonic-gate 	ucred->valid = 0;
521*0Sstevel@tonic-gate 	new->localcred = (caddr_t)ucred;
522*0Sstevel@tonic-gate 	new->key = *sessionkey;
523*0Sstevel@tonic-gate 	new->window = window;
524*0Sstevel@tonic-gate 	new->ref_time = gethrestime_sec();
525*0Sstevel@tonic-gate 	new->nickname = Nickname++;
526*0Sstevel@tonic-gate 	mutex_init(&new->lock, NULL, MUTEX_DEFAULT, NULL);
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	/* put new into the hash table */
529*0Sstevel@tonic-gate 	index = HASH(new->nickname);
530*0Sstevel@tonic-gate 	head = authdes_cache[index];
531*0Sstevel@tonic-gate 	if ((new->next = head) != NULL) {
532*0Sstevel@tonic-gate 		head->prev = new;
533*0Sstevel@tonic-gate 	}
534*0Sstevel@tonic-gate 	authdes_cache[index] = new;
535*0Sstevel@tonic-gate 	new->prev = NULL;
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 	/* update the LRU list */
538*0Sstevel@tonic-gate 	new->lru_prev = NULL;
539*0Sstevel@tonic-gate 	if ((new->lru_next = lru_first) != NULL) {
540*0Sstevel@tonic-gate 		lru_first->lru_prev = new;
541*0Sstevel@tonic-gate 	} else {
542*0Sstevel@tonic-gate 		lru_last = new;
543*0Sstevel@tonic-gate 	}
544*0Sstevel@tonic-gate 	lru_first = new;
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 	authdes_ncache++;
547*0Sstevel@tonic-gate 	return (new);
548*0Sstevel@tonic-gate }
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate /*
551*0Sstevel@tonic-gate  * Get an existing cache entry from authdes_cache table.
552*0Sstevel@tonic-gate  * The caller should have locked the authdes_cache table.
553*0Sstevel@tonic-gate  */
554*0Sstevel@tonic-gate struct authdes_cache_entry *
555*0Sstevel@tonic-gate authdes_cache_get(uint32_t nickname) {
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate 	struct authdes_cache_entry *cur = NULL;
558*0Sstevel@tonic-gate 	int index = HASH(nickname);
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
561*0Sstevel@tonic-gate 	for (cur = authdes_cache[index]; cur; cur = cur->next) {
562*0Sstevel@tonic-gate 		if ((cur->nickname == nickname)) {
563*0Sstevel@tonic-gate 			/* find it, update the LRU list */
564*0Sstevel@tonic-gate 			if (cur != lru_first) {
565*0Sstevel@tonic-gate 			    cur->lru_prev->lru_next = cur->lru_next;
566*0Sstevel@tonic-gate 			    if (cur->lru_next != NULL) {
567*0Sstevel@tonic-gate 				cur->lru_next->lru_prev = cur->lru_prev;
568*0Sstevel@tonic-gate 			    } else {
569*0Sstevel@tonic-gate 				lru_last = cur->lru_prev;
570*0Sstevel@tonic-gate 			    }
571*0Sstevel@tonic-gate 			    cur->lru_prev = NULL;
572*0Sstevel@tonic-gate 			    cur->lru_next = lru_first;
573*0Sstevel@tonic-gate 			    lru_first->lru_prev = cur;
574*0Sstevel@tonic-gate 			    lru_first = cur;
575*0Sstevel@tonic-gate 			}
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate 			cur->ref_time = gethrestime_sec();
578*0Sstevel@tonic-gate 			authdes_ncachehits++;
579*0Sstevel@tonic-gate 			return (cur);
580*0Sstevel@tonic-gate 		}
581*0Sstevel@tonic-gate 	}
582*0Sstevel@tonic-gate 
583*0Sstevel@tonic-gate 	authdes_ncachemisses++;
584*0Sstevel@tonic-gate 	return (NULL);
585*0Sstevel@tonic-gate }
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate /*
588*0Sstevel@tonic-gate  * authdes_cache_reclaim() is called by the kernel memory allocator
589*0Sstevel@tonic-gate  * when memory is low. This routine will reclaim 25% of the least recent
590*0Sstevel@tonic-gate  * used cache entries above the low water mark (low_cache_entries).
591*0Sstevel@tonic-gate  * If the cache entries have already hit the low water mark, it will
592*0Sstevel@tonic-gate  * return 1 cache entry.
593*0Sstevel@tonic-gate  */
594*0Sstevel@tonic-gate /*ARGSUSED*/
595*0Sstevel@tonic-gate void
596*0Sstevel@tonic-gate authdes_cache_reclaim(void *pdata) {
597*0Sstevel@tonic-gate 	struct authdes_cache_entry *p;
598*0Sstevel@tonic-gate 	int n, i;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	mutex_enter(&authdes_lock);
601*0Sstevel@tonic-gate 	n = authdes_ncache - low_cache_entries;
602*0Sstevel@tonic-gate 	n = n > 0 ? n/4 : 1;
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
605*0Sstevel@tonic-gate 		if ((p = lru_last) == lru_first)
606*0Sstevel@tonic-gate 			break;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 		/* Update the hash linked list */
609*0Sstevel@tonic-gate 		if (p->prev == NULL) {
610*0Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
611*0Sstevel@tonic-gate 		} else {
612*0Sstevel@tonic-gate 			p->prev->next = p->next;
613*0Sstevel@tonic-gate 		}
614*0Sstevel@tonic-gate 		if (p->next != NULL) {
615*0Sstevel@tonic-gate 			p->next->prev = p->prev;
616*0Sstevel@tonic-gate 		}
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 		/* update the LRU linked list */
619*0Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
620*0Sstevel@tonic-gate 		lru_last = p->lru_prev;
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
623*0Sstevel@tonic-gate 		kmem_free(p->localcred, sizeof (struct bsdcred));
624*0Sstevel@tonic-gate 		mutex_destroy(&p->lock);
625*0Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
626*0Sstevel@tonic-gate 
627*0Sstevel@tonic-gate 		authdes_ncache--;
628*0Sstevel@tonic-gate 	}
629*0Sstevel@tonic-gate 	mutex_exit(&authdes_lock);
630*0Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n",
631*0Sstevel@tonic-gate 				authdes_ncache);
632*0Sstevel@tonic-gate }
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate /*
635*0Sstevel@tonic-gate  *  Walk through the LRU doubly-linked list and delete the cache
636*0Sstevel@tonic-gate  *  entries that have not been used for more than authdes_cache_time.
637*0Sstevel@tonic-gate  *
638*0Sstevel@tonic-gate  *  Caller should have locked the cache table.
639*0Sstevel@tonic-gate  */
640*0Sstevel@tonic-gate void
641*0Sstevel@tonic-gate sweep_cache() {
642*0Sstevel@tonic-gate 	struct authdes_cache_entry *p;
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&authdes_lock));
645*0Sstevel@tonic-gate 	while ((p = lru_last) != lru_first) {
646*0Sstevel@tonic-gate 		IS_ALIGNED(p);
647*0Sstevel@tonic-gate 		NOT_DEAD(p);
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate 		/*
650*0Sstevel@tonic-gate 		 * If the last LRU entry idled less than authdes_cache_time,
651*0Sstevel@tonic-gate 		 * we are done with the sweeping.
652*0Sstevel@tonic-gate 		 */
653*0Sstevel@tonic-gate 		if (p->ref_time + authdes_cache_time > gethrestime_sec())
654*0Sstevel@tonic-gate 			break;
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 		/* update the hash linked list */
657*0Sstevel@tonic-gate 		if (p->prev == NULL) {
658*0Sstevel@tonic-gate 			authdes_cache[HASH(p->nickname)] = p->next;
659*0Sstevel@tonic-gate 		} else {
660*0Sstevel@tonic-gate 			p->prev->next = p->next;
661*0Sstevel@tonic-gate 		}
662*0Sstevel@tonic-gate 		if (p->next != NULL) {
663*0Sstevel@tonic-gate 			p->next->prev = p->prev;
664*0Sstevel@tonic-gate 		}
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 		/* update the LRU linked list */
667*0Sstevel@tonic-gate 		p->lru_prev->lru_next = NULL;
668*0Sstevel@tonic-gate 		lru_last = p->lru_prev;
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 		kmem_free(p->rname, strlen(p->rname) + 1);
671*0Sstevel@tonic-gate 		kmem_free(p->localcred, sizeof (struct bsdcred));
672*0Sstevel@tonic-gate 		mutex_destroy(&p->lock);
673*0Sstevel@tonic-gate 		kmem_cache_free(authdes_cache_handle, p);
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate 		authdes_ncache--;
676*0Sstevel@tonic-gate 	}
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate 	authdes_last_swept = gethrestime_sec();
679*0Sstevel@tonic-gate 	RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n",
680*0Sstevel@tonic-gate 				authdes_ncache);
681*0Sstevel@tonic-gate }
682