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