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
567dbe2beSCasper H.S. Dik  * Common Development and Distribution License (the "License").
667dbe2beSCasper H.S. Dik  * 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
2061961e0fSrobinson  */
2161961e0fSrobinson 
2261961e0fSrobinson /*
2367dbe2beSCasper H.S. Dik  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2548bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
26*b1cdc720SAlex Wilson  * Copyright 2017 Joyent Inc
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
297c478bd9Sstevel@tonic-gate /* All Rights Reserved */
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  * Portions of this source code were derived from Berkeley
327c478bd9Sstevel@tonic-gate  * 4.3 BSD under license from the Regents of the University of
337c478bd9Sstevel@tonic-gate  * California.
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate /*
377c478bd9Sstevel@tonic-gate  * svcauth_des.c, server-side des authentication
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * We insure for the service the following:
407c478bd9Sstevel@tonic-gate  * (1) The timestamp microseconds do not exceed 1 million.
417c478bd9Sstevel@tonic-gate  * (2) The timestamp plus the window is less than the current time.
427c478bd9Sstevel@tonic-gate  * (3) The timestamp is not less than the one previously
437c478bd9Sstevel@tonic-gate  *	seen in the current session.
447c478bd9Sstevel@tonic-gate  *
457c478bd9Sstevel@tonic-gate  * It is up to the server to determine if the window size is
467c478bd9Sstevel@tonic-gate  * too small.
477c478bd9Sstevel@tonic-gate  *
487c478bd9Sstevel@tonic-gate  */
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include "mt.h"
517c478bd9Sstevel@tonic-gate #include "rpc_mt.h"
527c478bd9Sstevel@tonic-gate #include <assert.h>
537c478bd9Sstevel@tonic-gate #include <rpc/des_crypt.h>
547c478bd9Sstevel@tonic-gate #include <rpc/rpc.h>
557c478bd9Sstevel@tonic-gate #include <sys/types.h>
5667dbe2beSCasper H.S. Dik #include <sys/param.h>
577c478bd9Sstevel@tonic-gate #include <stdlib.h>
587c478bd9Sstevel@tonic-gate #include <unistd.h>
597c478bd9Sstevel@tonic-gate #include <string.h>
6061961e0fSrobinson #include <strings.h>
61*b1cdc720SAlex Wilson #include <sys/debug.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #include <syslog.h>
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate extern int key_decryptsession_pk(const char *, netobj *, des_block *);
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate #define	USEC_PER_SEC	((ulong_t)1000000L)
687c478bd9Sstevel@tonic-gate #define	BEFORE(t1, t2) timercmp(t1, t2, < /* EMPTY */)
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate  * LRU cache of conversation keys and some other useful items.
737c478bd9Sstevel@tonic-gate  */
747c478bd9Sstevel@tonic-gate #define	DEF_AUTHDES_CACHESZ 128
757c478bd9Sstevel@tonic-gate int authdes_cachesz = DEF_AUTHDES_CACHESZ;
767c478bd9Sstevel@tonic-gate struct cache_entry {
777c478bd9Sstevel@tonic-gate 	des_block key;			/* conversation key */
787c478bd9Sstevel@tonic-gate 	char *rname;			/* client's name */
797c478bd9Sstevel@tonic-gate 	uint_t window;			/* credential lifetime window */
807c478bd9Sstevel@tonic-gate 	struct timeval laststamp;	/* detect replays of creds */
817c478bd9Sstevel@tonic-gate 	char *localcred;		/* generic local credential */
827c478bd9Sstevel@tonic-gate 	int index;			/* where are we in array? */
837c478bd9Sstevel@tonic-gate 	struct cache_entry *prev;	/* prev entry on LRU list */
847c478bd9Sstevel@tonic-gate 	struct cache_entry *next;	/* next entry on LRU list */
857c478bd9Sstevel@tonic-gate };
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate static const char __getucredstr[] = "authdes_getucred:";
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate static struct cache_entry *_rpc_authdes_cache;	/* [authdes_cachesz] */
907c478bd9Sstevel@tonic-gate static struct cache_entry *cache_head;	/* cache (in LRU order) */
917c478bd9Sstevel@tonic-gate static struct cache_entry *cache_tail;	/* cache (in LRU order) */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  *	A rwlock_t would seem to make more sense, but it turns out we always
957c478bd9Sstevel@tonic-gate  *	muck with the cache entries, so would always need a write lock (in
967c478bd9Sstevel@tonic-gate  *	which case, we might as well use a mutex).
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate extern mutex_t	authdes_lock;
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate static int cache_init(void);		/* initialize the cache */
1027c478bd9Sstevel@tonic-gate 					/* find an entry in the cache */
1037c478bd9Sstevel@tonic-gate static int cache_spot(des_block *, char *, struct timeval *);
1047c478bd9Sstevel@tonic-gate static void cache_ref(uint32_t);	/* note that sid was ref'd */
1057c478bd9Sstevel@tonic-gate static void invalidate(char *);		/* invalidate entry in cache */
1067c478bd9Sstevel@tonic-gate static void __msgout(int, const char *, const char *);
1077c478bd9Sstevel@tonic-gate static void __msgout2(const char *, const char *);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * cache statistics
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate struct {
1137c478bd9Sstevel@tonic-gate 	ulong_t ncachehits;	/* times cache hit, and is not replay */
1147c478bd9Sstevel@tonic-gate 	ulong_t ncachereplays;	/* times cache hit, and is replay */
1157c478bd9Sstevel@tonic-gate 	ulong_t ncachemisses;	/* times cache missed */
1167c478bd9Sstevel@tonic-gate } svcauthdes_stats;
1177c478bd9Sstevel@tonic-gate 
118*b1cdc720SAlex Wilson /*
119*b1cdc720SAlex Wilson  * NOTE: this has to fit inside RQCRED_SIZE bytes. If you update this struct,
120*b1cdc720SAlex Wilson  * double-check it still fits.
121*b1cdc720SAlex Wilson  */
122*b1cdc720SAlex Wilson struct authdes_area {
123*b1cdc720SAlex Wilson 	struct authdes_cred area_cred;
124*b1cdc720SAlex Wilson 	char area_netname[MAXNETNAMELEN+1];
125*b1cdc720SAlex Wilson };
126*b1cdc720SAlex Wilson CTASSERT(sizeof (struct authdes_area) <= RQCRED_SIZE);
127*b1cdc720SAlex Wilson 
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate  * Service side authenticator for AUTH_DES
1307c478bd9Sstevel@tonic-gate  */
1317c478bd9Sstevel@tonic-gate enum auth_stat
__svcauth_des(struct svc_req * rqst,struct rpc_msg * msg)1327c478bd9Sstevel@tonic-gate __svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
1337c478bd9Sstevel@tonic-gate {
1347c478bd9Sstevel@tonic-gate 	int32_t		*ixdr;
1357c478bd9Sstevel@tonic-gate 	des_block	cryptbuf[2];
1367c478bd9Sstevel@tonic-gate 	struct authdes_cred	*cred;
1377c478bd9Sstevel@tonic-gate 	struct authdes_verf	verf;
1387c478bd9Sstevel@tonic-gate 	int	status;
1397c478bd9Sstevel@tonic-gate 	struct cache_entry	*entry;
1407c478bd9Sstevel@tonic-gate 	uint32_t sid;
1417c478bd9Sstevel@tonic-gate 	int cache_spot_id;
1427c478bd9Sstevel@tonic-gate 	des_block	*sessionkey, init_sessionkey;
1437c478bd9Sstevel@tonic-gate 	des_block	ivec;
1447c478bd9Sstevel@tonic-gate 	uint_t	window;
145*b1cdc720SAlex Wilson 	struct authdes_area *area;
1467c478bd9Sstevel@tonic-gate 	struct timeval	timestamp;
1477c478bd9Sstevel@tonic-gate 	uint32_t	namelen;
1487c478bd9Sstevel@tonic-gate 	int	fullname_rcvd = 0;
1497c478bd9Sstevel@tonic-gate 	int from_cache = 0;
1507c478bd9Sstevel@tonic-gate 
15161961e0fSrobinson 	(void) mutex_lock(&authdes_lock);
1527c478bd9Sstevel@tonic-gate 	if (_rpc_authdes_cache == NULL) {
1537c478bd9Sstevel@tonic-gate 		int ret = cache_init();
1547c478bd9Sstevel@tonic-gate 		if (ret == -1) {
15561961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
1567c478bd9Sstevel@tonic-gate 			return (AUTH_FAILED);
1577c478bd9Sstevel@tonic-gate 		}
1587c478bd9Sstevel@tonic-gate 	}
15961961e0fSrobinson 	(void) mutex_unlock(&authdes_lock);
1607c478bd9Sstevel@tonic-gate 
16161961e0fSrobinson 	/* LINTED pointer cast */
162*b1cdc720SAlex Wilson 	area = (struct authdes_area *)rqst->rq_clntcred;
1637c478bd9Sstevel@tonic-gate 	cred = (struct authdes_cred *)&area->area_cred;
1647c478bd9Sstevel@tonic-gate 
16561961e0fSrobinson 	if ((uint_t)msg->rm_call.cb_cred.oa_length == 0)
1667c478bd9Sstevel@tonic-gate 		return (AUTH_BADCRED);
1677c478bd9Sstevel@tonic-gate 	/*
1687c478bd9Sstevel@tonic-gate 	 * Get the credential
1697c478bd9Sstevel@tonic-gate 	 */
17061961e0fSrobinson 	/* LINTED pointer cast */
1717c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
1727c478bd9Sstevel@tonic-gate 	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
1737c478bd9Sstevel@tonic-gate 	switch (cred->adc_namekind) {
1747c478bd9Sstevel@tonic-gate 	case ADN_FULLNAME:
1757c478bd9Sstevel@tonic-gate 		namelen = IXDR_GET_U_INT32(ixdr);
17661961e0fSrobinson 		if (namelen > MAXNETNAMELEN)
1777c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);
1787c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name = area->area_netname;
17961961e0fSrobinson 		(void) memcpy(cred->adc_fullname.name, ixdr, (uint_t)namelen);
1807c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name[namelen] = 0;
1817c478bd9Sstevel@tonic-gate 		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
1827c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
1837c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
1847c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = (uint32_t)*ixdr++;
1857c478bd9Sstevel@tonic-gate 		fullname_rcvd++;
1867c478bd9Sstevel@tonic-gate 		break;
1877c478bd9Sstevel@tonic-gate 	case ADN_NICKNAME:
1887c478bd9Sstevel@tonic-gate 		cred->adc_nickname = (uint32_t)*ixdr++;
1897c478bd9Sstevel@tonic-gate 		break;
1907c478bd9Sstevel@tonic-gate 	default:
1917c478bd9Sstevel@tonic-gate 		return (AUTH_BADCRED);
1927c478bd9Sstevel@tonic-gate 	}
1937c478bd9Sstevel@tonic-gate 
19461961e0fSrobinson 	if ((uint_t)msg->rm_call.cb_verf.oa_length == 0)
1957c478bd9Sstevel@tonic-gate 		return (AUTH_BADVERF);
1967c478bd9Sstevel@tonic-gate 	/*
1977c478bd9Sstevel@tonic-gate 	 * Get the verifier
1987c478bd9Sstevel@tonic-gate 	 */
19961961e0fSrobinson 	/* LINTED pointer cast */
2007c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
2017c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
2027c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp.key.low = (uint32_t)*ixdr++;
2037c478bd9Sstevel@tonic-gate 	verf.adv_int_u = (uint32_t)*ixdr++;
2047c478bd9Sstevel@tonic-gate 
20561961e0fSrobinson 	(void) mutex_lock(&authdes_lock);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	/*
2087c478bd9Sstevel@tonic-gate 	 * Get the conversation key
2097c478bd9Sstevel@tonic-gate 	 */
2107c478bd9Sstevel@tonic-gate 	if (fullname_rcvd) {	/* ADN_FULLNAME */
2117c478bd9Sstevel@tonic-gate 		netobj	pkey;
2127c478bd9Sstevel@tonic-gate 		char	pkey_data[1024];
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate again:
2157c478bd9Sstevel@tonic-gate 		init_sessionkey = cred->adc_fullname.key;
2167c478bd9Sstevel@tonic-gate 		sessionkey = &init_sessionkey;
2177c478bd9Sstevel@tonic-gate 
21861961e0fSrobinson 		if (!__getpublickey_cached(cred->adc_fullname.name,
2197c478bd9Sstevel@tonic-gate 				pkey_data, &from_cache)) {
2207c478bd9Sstevel@tonic-gate 			/*
22148bbca81SDaniel Hoffman 			 * if the user has no public key, treat them as the
2227c478bd9Sstevel@tonic-gate 			 * unauthenticated identity - nobody. If this
2237c478bd9Sstevel@tonic-gate 			 * works, it means the client didn't find the
2247c478bd9Sstevel@tonic-gate 			 * user's keys and used nobody's secret key
2257c478bd9Sstevel@tonic-gate 			 * as a backup.
2267c478bd9Sstevel@tonic-gate 			 */
22761961e0fSrobinson 			if (!__getpublickey_cached("nobody",
2287c478bd9Sstevel@tonic-gate 						pkey_data, &from_cache)) {
2297c478bd9Sstevel@tonic-gate 				__msgout(LOG_INFO,
2307c478bd9Sstevel@tonic-gate 				"_svcauth_des: no public key for nobody or ",
2317c478bd9Sstevel@tonic-gate 				cred->adc_fullname.name);
23261961e0fSrobinson 				(void) mutex_unlock(&authdes_lock);
2337c478bd9Sstevel@tonic-gate 				return (AUTH_BADCRED); /* no key */
2347c478bd9Sstevel@tonic-gate 			}
23561961e0fSrobinson 
23661961e0fSrobinson 			/*
23761961e0fSrobinson 			 * found a public key for nobody. change
23861961e0fSrobinson 			 * the fullname id to nobody, so the caller
23961961e0fSrobinson 			 * thinks the client specified nobody
24061961e0fSrobinson 			 * as the user identity.
24161961e0fSrobinson 			 */
24261961e0fSrobinson 			(void) strcpy(cred->adc_fullname.name, "nobody");
2437c478bd9Sstevel@tonic-gate 		}
2447c478bd9Sstevel@tonic-gate 		pkey.n_bytes = pkey_data;
2457c478bd9Sstevel@tonic-gate 		pkey.n_len = strlen(pkey_data) + 1;
2467c478bd9Sstevel@tonic-gate 		if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
2477c478bd9Sstevel@tonic-gate 				sessionkey) < 0) {
2487c478bd9Sstevel@tonic-gate 			if (from_cache) {
2497c478bd9Sstevel@tonic-gate 				__getpublickey_flush(cred->adc_fullname.name);
2507c478bd9Sstevel@tonic-gate 				goto again;
2517c478bd9Sstevel@tonic-gate 			}
25261961e0fSrobinson 			__msgout(LOG_INFO,
25361961e0fSrobinson 			    "_svcauth_des: key_decryptsessionkey failed for",
25461961e0fSrobinson 			    cred->adc_fullname.name);
25561961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
2567c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* key not found */
2577c478bd9Sstevel@tonic-gate 		}
2587c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
2597c478bd9Sstevel@tonic-gate 		sid = cred->adc_nickname;
2607c478bd9Sstevel@tonic-gate 		if (sid >= authdes_cachesz) {
2617c478bd9Sstevel@tonic-gate 			__msgout(LOG_INFO, "_svcauth_des:", "bad nickname");
26261961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
2637c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* garbled credential */
2647c478bd9Sstevel@tonic-gate 		}
2657c478bd9Sstevel@tonic-gate 		/* actually check that the entry is not null */
2667c478bd9Sstevel@tonic-gate 		entry = &_rpc_authdes_cache[sid];
2677c478bd9Sstevel@tonic-gate 		if (entry->rname == NULL) {
26861961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
2697c478bd9Sstevel@tonic-gate 			return (AUTH_BADCRED);	/* cached out */
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 		sessionkey = &_rpc_authdes_cache[sid].key;
2727c478bd9Sstevel@tonic-gate 	}
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	/*
2757c478bd9Sstevel@tonic-gate 	 * Decrypt the timestamp
2767c478bd9Sstevel@tonic-gate 	 */
2777c478bd9Sstevel@tonic-gate 	cryptbuf[0] = verf.adv_xtimestamp;
2787c478bd9Sstevel@tonic-gate 	if (fullname_rcvd) {	/* ADN_FULLNAME */
2797c478bd9Sstevel@tonic-gate 		cryptbuf[1].key.high = cred->adc_fullname.window;
2807c478bd9Sstevel@tonic-gate 		cryptbuf[1].key.low = verf.adv_winverf;
2817c478bd9Sstevel@tonic-gate 		ivec.key.high = ivec.key.low = 0;
2827c478bd9Sstevel@tonic-gate 		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
2837c478bd9Sstevel@tonic-gate 			2 * (int)sizeof (des_block), DES_DECRYPT | DES_HW,
2847c478bd9Sstevel@tonic-gate 			(char *)&ivec);
2857c478bd9Sstevel@tonic-gate 	} else {
2867c478bd9Sstevel@tonic-gate 		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
2877c478bd9Sstevel@tonic-gate 			(int)sizeof (des_block), DES_DECRYPT | DES_HW);
2887c478bd9Sstevel@tonic-gate 	}
2897c478bd9Sstevel@tonic-gate 	if (DES_FAILED(status)) {
2907c478bd9Sstevel@tonic-gate 		if (fullname_rcvd && from_cache) {
2917c478bd9Sstevel@tonic-gate 			__getpublickey_flush(cred->adc_fullname.name);
2927c478bd9Sstevel@tonic-gate 			goto again;
2937c478bd9Sstevel@tonic-gate 		}
2947c478bd9Sstevel@tonic-gate 		__msgout(LOG_ERR, "_svcauth_des: DES decryption failure for",
2957c478bd9Sstevel@tonic-gate 			fullname_rcvd ? cred->adc_fullname.name :
2967c478bd9Sstevel@tonic-gate 			_rpc_authdes_cache[sid].rname);
29761961e0fSrobinson 		(void) mutex_unlock(&authdes_lock);
2987c478bd9Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
2997c478bd9Sstevel@tonic-gate 	}
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	/*
3027c478bd9Sstevel@tonic-gate 	 * XDR the decrypted timestamp
3037c478bd9Sstevel@tonic-gate 	 */
3047c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
3057c478bd9Sstevel@tonic-gate 	timestamp.tv_sec = IXDR_GET_INT32(ixdr);
3067c478bd9Sstevel@tonic-gate 	timestamp.tv_usec = IXDR_GET_INT32(ixdr);
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	/*
3097c478bd9Sstevel@tonic-gate 	 * Check for valid credentials and verifiers.
3107c478bd9Sstevel@tonic-gate 	 * They could be invalid because the key was flushed
3117c478bd9Sstevel@tonic-gate 	 * out of the cache, and so a new session should begin.
3127c478bd9Sstevel@tonic-gate 	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
3137c478bd9Sstevel@tonic-gate 	 */
3147c478bd9Sstevel@tonic-gate 	{
3157c478bd9Sstevel@tonic-gate 		struct timeval current;
3167c478bd9Sstevel@tonic-gate 		int	nick;
3177c478bd9Sstevel@tonic-gate 		int	winverf;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 		if (fullname_rcvd) {
3207c478bd9Sstevel@tonic-gate 			window = IXDR_GET_U_INT32(ixdr);
3217c478bd9Sstevel@tonic-gate 			winverf = IXDR_GET_U_INT32(ixdr);
3227c478bd9Sstevel@tonic-gate 			if (winverf != window - 1) {
3237c478bd9Sstevel@tonic-gate 				if (from_cache) {
3247c478bd9Sstevel@tonic-gate 					__getpublickey_flush(
3257c478bd9Sstevel@tonic-gate 						cred->adc_fullname.name);
3267c478bd9Sstevel@tonic-gate 					goto again;
3277c478bd9Sstevel@tonic-gate 				}
3287c478bd9Sstevel@tonic-gate 				__msgout(LOG_INFO,
3297c478bd9Sstevel@tonic-gate 					"_svcauth_des: corrupted window from",
3307c478bd9Sstevel@tonic-gate 					cred->adc_fullname.name);
33161961e0fSrobinson 				(void) mutex_unlock(&authdes_lock);
3327c478bd9Sstevel@tonic-gate 				/* garbled credential or invalid secret key */
3337c478bd9Sstevel@tonic-gate 				return (AUTH_BADCRED);
3347c478bd9Sstevel@tonic-gate 			}
3357c478bd9Sstevel@tonic-gate 			cache_spot_id = cache_spot(sessionkey,
3367c478bd9Sstevel@tonic-gate 						cred->adc_fullname.name,
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 					&timestamp);
3397c478bd9Sstevel@tonic-gate 			if (cache_spot_id < 0) {
3407c478bd9Sstevel@tonic-gate 			__msgout(LOG_INFO,
3417c478bd9Sstevel@tonic-gate 				"_svcauth_des: replayed credential from",
3427c478bd9Sstevel@tonic-gate 				cred->adc_fullname.name);
34361961e0fSrobinson 				(void) mutex_unlock(&authdes_lock);
3447c478bd9Sstevel@tonic-gate 				return (AUTH_REJECTEDCRED);	/* replay */
3457c478bd9Sstevel@tonic-gate 			} else sid = cache_spot_id;
3467c478bd9Sstevel@tonic-gate 			nick = 0;
3477c478bd9Sstevel@tonic-gate 		} else {	/* ADN_NICKNAME */
3487c478bd9Sstevel@tonic-gate 			window = _rpc_authdes_cache[sid].window;
3497c478bd9Sstevel@tonic-gate 			nick = 1;
3507c478bd9Sstevel@tonic-gate 		}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 		if ((ulong_t)timestamp.tv_usec >= USEC_PER_SEC) {
3537c478bd9Sstevel@tonic-gate 			if (fullname_rcvd && from_cache) {
3547c478bd9Sstevel@tonic-gate 				__getpublickey_flush(cred->adc_fullname.name);
3557c478bd9Sstevel@tonic-gate 				goto again;
3567c478bd9Sstevel@tonic-gate 			}
3577c478bd9Sstevel@tonic-gate 		__msgout(LOG_INFO,
3587c478bd9Sstevel@tonic-gate 			"_svcauth_des: invalid timestamp received from",
3597c478bd9Sstevel@tonic-gate 			fullname_rcvd ? cred->adc_fullname.name :
3607c478bd9Sstevel@tonic-gate 				_rpc_authdes_cache[sid].rname);
3617c478bd9Sstevel@tonic-gate 			/* cached out (bad key), or garbled verifier */
36261961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
3637c478bd9Sstevel@tonic-gate 			return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
3647c478bd9Sstevel@tonic-gate 		}
3657c478bd9Sstevel@tonic-gate 		if (nick && BEFORE(&timestamp,
3667c478bd9Sstevel@tonic-gate 				&_rpc_authdes_cache[sid].laststamp)) {
3677c478bd9Sstevel@tonic-gate 			if (fullname_rcvd && from_cache) {
3687c478bd9Sstevel@tonic-gate 				__getpublickey_flush(cred->adc_fullname.name);
3697c478bd9Sstevel@tonic-gate 				goto again;
3707c478bd9Sstevel@tonic-gate 			}
3717c478bd9Sstevel@tonic-gate 			__msgout(LOG_INFO,
3727c478bd9Sstevel@tonic-gate 	"_svcauth_des: timestamp is earlier than the one previously seen from",
3737c478bd9Sstevel@tonic-gate 			fullname_rcvd ? cred->adc_fullname.name :
3747c478bd9Sstevel@tonic-gate 				_rpc_authdes_cache[sid].rname);
37561961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
3767c478bd9Sstevel@tonic-gate 			return (AUTH_REJECTEDVERF);	/* replay */
3777c478bd9Sstevel@tonic-gate 		}
37861961e0fSrobinson 		(void) gettimeofday(&current, NULL);
3797c478bd9Sstevel@tonic-gate 		current.tv_sec -= window;	/* allow for expiration */
3807c478bd9Sstevel@tonic-gate 		if (!BEFORE(&current, &timestamp)) {
3817c478bd9Sstevel@tonic-gate 			if (fullname_rcvd && from_cache) {
3827c478bd9Sstevel@tonic-gate 				__getpublickey_flush(cred->adc_fullname.name);
3837c478bd9Sstevel@tonic-gate 				goto again;
3847c478bd9Sstevel@tonic-gate 			}
3857c478bd9Sstevel@tonic-gate 			__msgout(LOG_INFO,
3867c478bd9Sstevel@tonic-gate 				"_svcauth_des: timestamp expired for",
3877c478bd9Sstevel@tonic-gate 				fullname_rcvd ? cred->adc_fullname.name :
3887c478bd9Sstevel@tonic-gate 					_rpc_authdes_cache[sid].rname);
3897c478bd9Sstevel@tonic-gate 			/* replay, or garbled credential */
39061961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
3917c478bd9Sstevel@tonic-gate 			return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
3927c478bd9Sstevel@tonic-gate 		}
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/*
3967c478bd9Sstevel@tonic-gate 	 * Set up the reply verifier
3977c478bd9Sstevel@tonic-gate 	 */
3987c478bd9Sstevel@tonic-gate 	verf.adv_nickname = sid;
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 	/*
4017c478bd9Sstevel@tonic-gate 	 * xdr the timestamp before encrypting
4027c478bd9Sstevel@tonic-gate 	 */
4037c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)cryptbuf;
4047c478bd9Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
4057c478bd9Sstevel@tonic-gate 	IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	/*
4087c478bd9Sstevel@tonic-gate 	 * encrypt the timestamp
4097c478bd9Sstevel@tonic-gate 	 */
4107c478bd9Sstevel@tonic-gate 	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
4117c478bd9Sstevel@tonic-gate 				(int)sizeof (des_block), DES_ENCRYPT | DES_HW);
4127c478bd9Sstevel@tonic-gate 	if (DES_FAILED(status)) {
4137c478bd9Sstevel@tonic-gate 		__msgout(LOG_ERR, "_svcauth_des: DES encryption failure for",
4147c478bd9Sstevel@tonic-gate 			fullname_rcvd ? cred->adc_fullname.name :
4157c478bd9Sstevel@tonic-gate 			_rpc_authdes_cache[sid].rname);
41661961e0fSrobinson 		(void) mutex_unlock(&authdes_lock);
4177c478bd9Sstevel@tonic-gate 		return (AUTH_FAILED);	/* system error */
4187c478bd9Sstevel@tonic-gate 	}
4197c478bd9Sstevel@tonic-gate 	verf.adv_xtimestamp = cryptbuf[0];
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	/*
4227c478bd9Sstevel@tonic-gate 	 * Serialize the reply verifier, and update rqst
4237c478bd9Sstevel@tonic-gate 	 */
42461961e0fSrobinson 	/* LINTED pointer cast */
4257c478bd9Sstevel@tonic-gate 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
4267c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
4277c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
4287c478bd9Sstevel@tonic-gate 	*ixdr++ = (int32_t)verf.adv_int_u;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
4317c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
4327c478bd9Sstevel@tonic-gate 	rqst->rq_xprt->xp_verf.oa_length =
4337c478bd9Sstevel@tonic-gate 		(char *)ixdr - msg->rm_call.cb_verf.oa_base;
4347c478bd9Sstevel@tonic-gate 	if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
4357c478bd9Sstevel@tonic-gate 		__msgout(LOG_ERR,
4367c478bd9Sstevel@tonic-gate 			"_svcauth_des: Authenticator length error",
4377c478bd9Sstevel@tonic-gate 			fullname_rcvd ? cred->adc_fullname.name :
4387c478bd9Sstevel@tonic-gate 			_rpc_authdes_cache[sid].rname);
43961961e0fSrobinson 		(void) mutex_unlock(&authdes_lock);
4407c478bd9Sstevel@tonic-gate 		return (AUTH_REJECTEDVERF);
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	/*
4447c478bd9Sstevel@tonic-gate 	 * We succeeded, commit the data to the cache now and
4457c478bd9Sstevel@tonic-gate 	 * finish cooking the credential.
4467c478bd9Sstevel@tonic-gate 	 */
4477c478bd9Sstevel@tonic-gate 	entry = &_rpc_authdes_cache[sid];
4487c478bd9Sstevel@tonic-gate 	entry->laststamp = timestamp;
4497c478bd9Sstevel@tonic-gate 	cache_ref(sid);
4507c478bd9Sstevel@tonic-gate 	if (cred->adc_namekind == ADN_FULLNAME) {
4517c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = window;
4527c478bd9Sstevel@tonic-gate 		cred->adc_nickname = sid;	/* save nickname */
45361961e0fSrobinson 		if (entry->rname != NULL)
45461961e0fSrobinson 			free(entry->rname);
45561961e0fSrobinson 		entry->rname = malloc(strlen(cred->adc_fullname.name) + 1);
4567c478bd9Sstevel@tonic-gate 		if (entry->rname != NULL) {
4577c478bd9Sstevel@tonic-gate 			(void) strcpy(entry->rname, cred->adc_fullname.name);
4587c478bd9Sstevel@tonic-gate 		} else {
4597c478bd9Sstevel@tonic-gate 			__msgout(LOG_CRIT, "_svcauth_des:", "out of memory");
46061961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
4617c478bd9Sstevel@tonic-gate 			return (AUTH_FAILED);
4627c478bd9Sstevel@tonic-gate 		}
4637c478bd9Sstevel@tonic-gate 		entry->key = *sessionkey;
4647c478bd9Sstevel@tonic-gate 		entry->window = window;
4657c478bd9Sstevel@tonic-gate 		/* mark any cached cred invalid */
4667c478bd9Sstevel@tonic-gate 		invalidate(entry->localcred);
4677c478bd9Sstevel@tonic-gate 	} else { /* ADN_NICKNAME */
4687c478bd9Sstevel@tonic-gate 		/*
4697c478bd9Sstevel@tonic-gate 		 * nicknames are cooked into fullnames
4707c478bd9Sstevel@tonic-gate 		 */
4717c478bd9Sstevel@tonic-gate 		cred->adc_namekind = ADN_FULLNAME;
4727c478bd9Sstevel@tonic-gate 		cred->adc_fullname.name = entry->rname;
4737c478bd9Sstevel@tonic-gate 		cred->adc_fullname.key = entry->key;
4747c478bd9Sstevel@tonic-gate 		cred->adc_fullname.window = entry->window;
4757c478bd9Sstevel@tonic-gate 	}
47661961e0fSrobinson 	(void) mutex_unlock(&authdes_lock);
4777c478bd9Sstevel@tonic-gate 	return (AUTH_OK);	/* we made it! */
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate  * Initialize the cache
4837c478bd9Sstevel@tonic-gate  */
4847c478bd9Sstevel@tonic-gate static int
cache_init(void)48561961e0fSrobinson cache_init(void)
4867c478bd9Sstevel@tonic-gate {
4877c478bd9Sstevel@tonic-gate 	int i;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate /* LOCK HELD ON ENTRY: authdes_lock */
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&authdes_lock));
49261961e0fSrobinson 	_rpc_authdes_cache =
49361961e0fSrobinson 		malloc(sizeof (struct cache_entry) * authdes_cachesz);
4947c478bd9Sstevel@tonic-gate 	if (_rpc_authdes_cache == NULL) {
4957c478bd9Sstevel@tonic-gate 		__msgout(LOG_CRIT, "cache_init:", "out of memory");
4967c478bd9Sstevel@tonic-gate 		return (-1);
4977c478bd9Sstevel@tonic-gate 	}
49861961e0fSrobinson 	(void) memset(_rpc_authdes_cache, 0,
4997c478bd9Sstevel@tonic-gate 		sizeof (struct cache_entry) * authdes_cachesz);
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	/*
5027c478bd9Sstevel@tonic-gate 	 * Initialize the lru chain (linked-list)
5037c478bd9Sstevel@tonic-gate 	 */
5047c478bd9Sstevel@tonic-gate 	for (i = 1; i < (authdes_cachesz - 1); i++) {
5057c478bd9Sstevel@tonic-gate 		_rpc_authdes_cache[i].index = i;
5067c478bd9Sstevel@tonic-gate 		_rpc_authdes_cache[i].next = &_rpc_authdes_cache[i + 1];
5077c478bd9Sstevel@tonic-gate 		_rpc_authdes_cache[i].prev = &_rpc_authdes_cache[i - 1];
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 	cache_head = &_rpc_authdes_cache[0];
5107c478bd9Sstevel@tonic-gate 	cache_tail = &_rpc_authdes_cache[authdes_cachesz - 1];
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	/*
5137c478bd9Sstevel@tonic-gate 	 * These elements of the chain need special attention...
5147c478bd9Sstevel@tonic-gate 	 */
5157c478bd9Sstevel@tonic-gate 	cache_head->index = 0;
5167c478bd9Sstevel@tonic-gate 	cache_tail->index = authdes_cachesz - 1;
5177c478bd9Sstevel@tonic-gate 	cache_head->next = &_rpc_authdes_cache[1];
5187c478bd9Sstevel@tonic-gate 	cache_head->prev = cache_tail;
5197c478bd9Sstevel@tonic-gate 	cache_tail->next = cache_head;
5207c478bd9Sstevel@tonic-gate 	cache_tail->prev = &_rpc_authdes_cache[authdes_cachesz - 2];
5217c478bd9Sstevel@tonic-gate 	return (0);
5227c478bd9Sstevel@tonic-gate }
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate /*
5267c478bd9Sstevel@tonic-gate  * Find the lru victim
5277c478bd9Sstevel@tonic-gate  */
5287c478bd9Sstevel@tonic-gate static uint32_t
cache_victim(void)52961961e0fSrobinson cache_victim(void)
5307c478bd9Sstevel@tonic-gate {
5317c478bd9Sstevel@tonic-gate /* LOCK HELD ON ENTRY: authdes_lock */
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&authdes_lock));
5347c478bd9Sstevel@tonic-gate 	return (cache_head->index);			/* list in lru order */
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate /*
5387c478bd9Sstevel@tonic-gate  * Note that sid was referenced
5397c478bd9Sstevel@tonic-gate  */
5407c478bd9Sstevel@tonic-gate static void
cache_ref(uint32_t sid)5417c478bd9Sstevel@tonic-gate cache_ref(uint32_t sid)
5427c478bd9Sstevel@tonic-gate {
5437c478bd9Sstevel@tonic-gate 	struct cache_entry *curr = &_rpc_authdes_cache[sid];
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate /* LOCK HELD ON ENTRY: authdes_lock */
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&authdes_lock));
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	/*
5517c478bd9Sstevel@tonic-gate 	 * move referenced item from its place on the LRU chain
5527c478bd9Sstevel@tonic-gate 	 * to the tail of the chain while checking for special
5537c478bd9Sstevel@tonic-gate 	 * conditions (mainly for performance).
5547c478bd9Sstevel@tonic-gate 	 */
5557c478bd9Sstevel@tonic-gate 	if (cache_tail == curr) {			/* no work to do */
5567c478bd9Sstevel@tonic-gate 		/*EMPTY*/;
5577c478bd9Sstevel@tonic-gate 	} else if (cache_head == curr) {
5587c478bd9Sstevel@tonic-gate 		cache_head = cache_head->next;
5597c478bd9Sstevel@tonic-gate 		cache_tail = curr;
5607c478bd9Sstevel@tonic-gate 	} else {
5617c478bd9Sstevel@tonic-gate 		(curr->next)->prev = curr->prev;	/* fix thy neighbor */
5627c478bd9Sstevel@tonic-gate 		(curr->prev)->next = curr->next;
5637c478bd9Sstevel@tonic-gate 		curr->next = cache_head;		/* fix thy self... */
5647c478bd9Sstevel@tonic-gate 		curr->prev = cache_tail;
5657c478bd9Sstevel@tonic-gate 		cache_head->prev = curr;		/* fix the head  */
5667c478bd9Sstevel@tonic-gate 		cache_tail->next = curr;		/* fix the tail  */
5677c478bd9Sstevel@tonic-gate 		cache_tail = curr;			/* move the tail */
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate }
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate /*
5727c478bd9Sstevel@tonic-gate  * Find a spot in the cache for a credential containing
5737c478bd9Sstevel@tonic-gate  * the items given. Return -1 if a replay is detected, otherwise
5747c478bd9Sstevel@tonic-gate  * return the spot in the cache.
5757c478bd9Sstevel@tonic-gate  */
5767c478bd9Sstevel@tonic-gate static int
cache_spot(des_block * key,char * name,struct timeval * timestamp)5777c478bd9Sstevel@tonic-gate cache_spot(des_block *key, char *name, struct timeval *timestamp)
5787c478bd9Sstevel@tonic-gate {
5797c478bd9Sstevel@tonic-gate 	struct cache_entry *cp;
5807c478bd9Sstevel@tonic-gate 	int i;
5817c478bd9Sstevel@tonic-gate 	uint32_t hi;
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /* LOCK HELD ON ENTRY: authdes_lock */
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&authdes_lock));
5867c478bd9Sstevel@tonic-gate 	hi = key->key.high;
5877c478bd9Sstevel@tonic-gate 	for (cp = _rpc_authdes_cache, i = 0; i < authdes_cachesz; i++, cp++) {
5887c478bd9Sstevel@tonic-gate 		if (cp->key.key.high == hi &&
5897c478bd9Sstevel@tonic-gate 		    cp->key.key.low == key->key.low &&
5907c478bd9Sstevel@tonic-gate 		    cp->rname != NULL &&
5917c478bd9Sstevel@tonic-gate 		    memcmp(cp->rname, name, strlen(name) + 1) == 0) {
5927c478bd9Sstevel@tonic-gate 			if (BEFORE(timestamp, &cp->laststamp)) {
5937c478bd9Sstevel@tonic-gate 				svcauthdes_stats.ncachereplays++;
5947c478bd9Sstevel@tonic-gate 				return (-1);	/* replay */
5957c478bd9Sstevel@tonic-gate 			}
5967c478bd9Sstevel@tonic-gate 			svcauthdes_stats.ncachehits++;
5977c478bd9Sstevel@tonic-gate 			return (i);
5987c478bd9Sstevel@tonic-gate 			/* refresh */
5997c478bd9Sstevel@tonic-gate 		}
6007c478bd9Sstevel@tonic-gate 	}
6017c478bd9Sstevel@tonic-gate 	svcauthdes_stats.ncachemisses++;
60261961e0fSrobinson 	return (cache_victim());
6037c478bd9Sstevel@tonic-gate }
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate /*
6077c478bd9Sstevel@tonic-gate  * Local credential handling stuff.
6087c478bd9Sstevel@tonic-gate  * NOTE: bsd unix dependent.
6097c478bd9Sstevel@tonic-gate  * Other operating systems should put something else here.
6107c478bd9Sstevel@tonic-gate  */
6117c478bd9Sstevel@tonic-gate #define	UNKNOWN 	-2	/* grouplen, if cached cred is unknown user */
6127c478bd9Sstevel@tonic-gate #define	INVALID		-1 	/* grouplen, if cache entry is invalid */
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate struct bsdcred {
6157c478bd9Sstevel@tonic-gate 	uid_t uid;		/* cached uid */
6167c478bd9Sstevel@tonic-gate 	gid_t gid;		/* cached gid */
6177c478bd9Sstevel@tonic-gate 	short grouplen;	/* length of cached groups */
61867dbe2beSCasper H.S. Dik 	gid_t groups[1];	/* cached groups allocate _SC_NGROUPS_MAX */
6197c478bd9Sstevel@tonic-gate };
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate static void
invalidate(char * cred)6227c478bd9Sstevel@tonic-gate invalidate(char *cred)
6237c478bd9Sstevel@tonic-gate {
62461961e0fSrobinson 	if (cred == NULL)
6257c478bd9Sstevel@tonic-gate 		return;
62661961e0fSrobinson 	/* LINTED pointer cast */
6277c478bd9Sstevel@tonic-gate 	((struct bsdcred *)cred)->grouplen = INVALID;
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate /*
6317c478bd9Sstevel@tonic-gate  * Map a des credential into a unix cred.
6327c478bd9Sstevel@tonic-gate  * We cache the credential here so the application does
6337c478bd9Sstevel@tonic-gate  * not have to make an rpc call every time to interpret
6347c478bd9Sstevel@tonic-gate  * the credential.
6357c478bd9Sstevel@tonic-gate  */
6367c478bd9Sstevel@tonic-gate int
authdes_getucred(const struct authdes_cred * adc,uid_t * uid,gid_t * gid,short * grouplen,gid_t * groups)6377c478bd9Sstevel@tonic-gate authdes_getucred(const struct authdes_cred *adc, uid_t *uid, gid_t *gid,
6387c478bd9Sstevel@tonic-gate     short *grouplen, gid_t *groups)
6397c478bd9Sstevel@tonic-gate {
6407c478bd9Sstevel@tonic-gate 	uint32_t sid;
6417c478bd9Sstevel@tonic-gate 	int i;
6427c478bd9Sstevel@tonic-gate 	uid_t i_uid;
6437c478bd9Sstevel@tonic-gate 	gid_t i_gid;
6447c478bd9Sstevel@tonic-gate 	int i_grouplen;
6457c478bd9Sstevel@tonic-gate 	struct bsdcred *cred;
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	sid = adc->adc_nickname;
6487c478bd9Sstevel@tonic-gate 	if (sid >= authdes_cachesz) {
6497c478bd9Sstevel@tonic-gate 		__msgout2(__getucredstr, "invalid nickname");
6507c478bd9Sstevel@tonic-gate 		return (0);
6517c478bd9Sstevel@tonic-gate 	}
65261961e0fSrobinson 	(void) mutex_lock(&authdes_lock);
65361961e0fSrobinson 	/* LINTED pointer cast */
6547c478bd9Sstevel@tonic-gate 	cred = (struct bsdcred *)_rpc_authdes_cache[sid].localcred;
6557c478bd9Sstevel@tonic-gate 	if (cred == NULL) {
65667dbe2beSCasper H.S. Dik 		static size_t bsdcred_sz;
65767dbe2beSCasper H.S. Dik 
65867dbe2beSCasper H.S. Dik 		if (bsdcred_sz == 0) {
65967dbe2beSCasper H.S. Dik 			bsdcred_sz = sizeof (struct bsdcred) +
66067dbe2beSCasper H.S. Dik 			    (sysconf(_SC_NGROUPS_MAX) - 1) * sizeof (gid_t);
66167dbe2beSCasper H.S. Dik 		}
66267dbe2beSCasper H.S. Dik 		cred = malloc(bsdcred_sz);
6637c478bd9Sstevel@tonic-gate 		if (cred == NULL) {
6647c478bd9Sstevel@tonic-gate 			__msgout2(__getucredstr, "out of memory");
66561961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
6667c478bd9Sstevel@tonic-gate 			return (0);
6677c478bd9Sstevel@tonic-gate 		}
6687c478bd9Sstevel@tonic-gate 		_rpc_authdes_cache[sid].localcred = (char *)cred;
6697c478bd9Sstevel@tonic-gate 		cred->grouplen = INVALID;
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 	if (cred->grouplen == INVALID) {
6727c478bd9Sstevel@tonic-gate 		/*
6737c478bd9Sstevel@tonic-gate 		 * not in cache: lookup
6747c478bd9Sstevel@tonic-gate 		 */
6757c478bd9Sstevel@tonic-gate 		if (!netname2user(adc->adc_fullname.name, (uid_t *)&i_uid,
6767c478bd9Sstevel@tonic-gate 			(gid_t *)&i_gid, &i_grouplen, (gid_t *)groups)) {
6777c478bd9Sstevel@tonic-gate 			__msgout2(__getucredstr, "unknown netname");
6787c478bd9Sstevel@tonic-gate 			/* mark as lookup up, but not found */
6797c478bd9Sstevel@tonic-gate 			cred->grouplen = UNKNOWN;
68061961e0fSrobinson 			(void) mutex_unlock(&authdes_lock);
6817c478bd9Sstevel@tonic-gate 			return (0);
6827c478bd9Sstevel@tonic-gate 		}
6837c478bd9Sstevel@tonic-gate 		__msgout2(__getucredstr, "missed ucred cache");
6847c478bd9Sstevel@tonic-gate 		*uid = cred->uid = i_uid;
6857c478bd9Sstevel@tonic-gate 		*gid = cred->gid = i_gid;
6867c478bd9Sstevel@tonic-gate 		*grouplen = cred->grouplen = i_grouplen;
6877c478bd9Sstevel@tonic-gate 		for (i = i_grouplen - 1; i >= 0; i--) {
68867dbe2beSCasper H.S. Dik 			cred->groups[i] = groups[i];
6897c478bd9Sstevel@tonic-gate 		}
69061961e0fSrobinson 		(void) mutex_unlock(&authdes_lock);
6917c478bd9Sstevel@tonic-gate 		return (1);
69261961e0fSrobinson 	}
69361961e0fSrobinson 	if (cred->grouplen == UNKNOWN) {
6947c478bd9Sstevel@tonic-gate 		/*
6957c478bd9Sstevel@tonic-gate 		 * Already lookup up, but no match found
6967c478bd9Sstevel@tonic-gate 		 */
69761961e0fSrobinson 		(void) mutex_unlock(&authdes_lock);
6987c478bd9Sstevel@tonic-gate 		return (0);
6997c478bd9Sstevel@tonic-gate 	}
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	/*
7027c478bd9Sstevel@tonic-gate 	 * cached credentials
7037c478bd9Sstevel@tonic-gate 	 */
7047c478bd9Sstevel@tonic-gate 	*uid = cred->uid;
7057c478bd9Sstevel@tonic-gate 	*gid = cred->gid;
7067c478bd9Sstevel@tonic-gate 	*grouplen = cred->grouplen;
7077c478bd9Sstevel@tonic-gate 	for (i = cred->grouplen - 1; i >= 0; i--) {
70867dbe2beSCasper H.S. Dik 		groups[i] = cred->groups[i];
7097c478bd9Sstevel@tonic-gate 	}
71061961e0fSrobinson 	(void) mutex_unlock(&authdes_lock);
7117c478bd9Sstevel@tonic-gate 	return (1);
7127c478bd9Sstevel@tonic-gate }
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate static void
__msgout(int level,const char * str,const char * strarg)7167c478bd9Sstevel@tonic-gate __msgout(int level, const char *str, const char *strarg)
7177c478bd9Sstevel@tonic-gate {
71861961e0fSrobinson 	(void) syslog(level, "%s %s", str, strarg);
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate static void
__msgout2(const char * str,const char * str2)7237c478bd9Sstevel@tonic-gate __msgout2(const char *str, const char *str2)
7247c478bd9Sstevel@tonic-gate {
72561961e0fSrobinson 	(void) syslog(LOG_DEBUG, "%s %s", str, str2);
7267c478bd9Sstevel@tonic-gate }
727