xref: /titanic_41/usr/src/lib/libnsl/rpc/svcauth_des.c (revision e962c73bf360d83d61290131b3e921ecd63a0a77)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2017 Joyent Inc
26  */
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 /*
36  * svcauth_des.c, server-side des authentication
37  *
38  * We insure for the service the following:
39  * (1) The timestamp microseconds do not exceed 1 million.
40  * (2) The timestamp plus the window is less than the current time.
41  * (3) The timestamp is not less than the one previously
42  *	seen in the current session.
43  *
44  * It is up to the server to determine if the window size is
45  * too small.
46  *
47  */
48 
49 #include "mt.h"
50 #include "rpc_mt.h"
51 #include <assert.h>
52 #include <rpc/des_crypt.h>
53 #include <rpc/rpc.h>
54 #include <sys/types.h>
55 #include <sys/param.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <string.h>
59 #include <strings.h>
60 #include <sys/debug.h>
61 
62 #include <syslog.h>
63 
64 extern int key_decryptsession_pk(const char *, netobj *, des_block *);
65 
66 #define	USEC_PER_SEC	((ulong_t)1000000L)
67 #define	BEFORE(t1, t2) timercmp(t1, t2, < /* EMPTY */)
68 
69 
70 /*
71  * LRU cache of conversation keys and some other useful items.
72  */
73 #define	DEF_AUTHDES_CACHESZ 128
74 int authdes_cachesz = DEF_AUTHDES_CACHESZ;
75 struct cache_entry {
76 	des_block key;			/* conversation key */
77 	char *rname;			/* client's name */
78 	uint_t window;			/* credential lifetime window */
79 	struct timeval laststamp;	/* detect replays of creds */
80 	char *localcred;		/* generic local credential */
81 	int index;			/* where are we in array? */
82 	struct cache_entry *prev;	/* prev entry on LRU list */
83 	struct cache_entry *next;	/* next entry on LRU list */
84 };
85 
86 static const char __getucredstr[] = "authdes_getucred:";
87 
88 static struct cache_entry *_rpc_authdes_cache;	/* [authdes_cachesz] */
89 static struct cache_entry *cache_head;	/* cache (in LRU order) */
90 static struct cache_entry *cache_tail;	/* cache (in LRU order) */
91 
92 /*
93  *	A rwlock_t would seem to make more sense, but it turns out we always
94  *	muck with the cache entries, so would always need a write lock (in
95  *	which case, we might as well use a mutex).
96  */
97 extern mutex_t	authdes_lock;
98 
99 
100 static int cache_init(void);		/* initialize the cache */
101 					/* find an entry in the cache */
102 static int cache_spot(des_block *, char *, struct timeval *);
103 static void cache_ref(uint32_t);	/* note that sid was ref'd */
104 static void invalidate(char *);		/* invalidate entry in cache */
105 static void __msgout(int, const char *, const char *);
106 static void __msgout2(const char *, const char *);
107 
108 /*
109  * cache statistics
110  */
111 struct {
112 	ulong_t ncachehits;	/* times cache hit, and is not replay */
113 	ulong_t ncachereplays;	/* times cache hit, and is replay */
114 	ulong_t ncachemisses;	/* times cache missed */
115 } svcauthdes_stats;
116 
117 /*
118  * NOTE: this has to fit inside RQCRED_SIZE bytes. If you update this struct,
119  * double-check it still fits.
120  */
121 struct authdes_area {
122 	struct authdes_cred area_cred;
123 	char area_netname[MAXNETNAMELEN+1];
124 };
125 CTASSERT(sizeof (struct authdes_area) <= RQCRED_SIZE);
126 
127 /*
128  * Service side authenticator for AUTH_DES
129  */
130 enum auth_stat
__svcauth_des(struct svc_req * rqst,struct rpc_msg * msg)131 __svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
132 {
133 	int32_t		*ixdr;
134 	des_block	cryptbuf[2];
135 	struct authdes_cred	*cred;
136 	struct authdes_verf	verf;
137 	int	status;
138 	struct cache_entry	*entry;
139 	uint32_t sid;
140 	int cache_spot_id;
141 	des_block	*sessionkey, init_sessionkey;
142 	des_block	ivec;
143 	uint_t	window;
144 	struct authdes_area *area;
145 	struct timeval	timestamp;
146 	uint32_t	namelen;
147 	int	fullname_rcvd = 0;
148 	int from_cache = 0;
149 
150 	(void) mutex_lock(&authdes_lock);
151 	if (_rpc_authdes_cache == NULL) {
152 		int ret = cache_init();
153 		if (ret == -1) {
154 			(void) mutex_unlock(&authdes_lock);
155 			return (AUTH_FAILED);
156 		}
157 	}
158 	(void) mutex_unlock(&authdes_lock);
159 
160 	/* LINTED pointer cast */
161 	area = (struct authdes_area *)rqst->rq_clntcred;
162 	cred = (struct authdes_cred *)&area->area_cred;
163 
164 	if ((uint_t)msg->rm_call.cb_cred.oa_length == 0)
165 		return (AUTH_BADCRED);
166 	/*
167 	 * Get the credential
168 	 */
169 	/* LINTED pointer cast */
170 	ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
171 	cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
172 	switch (cred->adc_namekind) {
173 	case ADN_FULLNAME:
174 		namelen = IXDR_GET_U_INT32(ixdr);
175 		if (namelen > MAXNETNAMELEN)
176 			return (AUTH_BADCRED);
177 		cred->adc_fullname.name = area->area_netname;
178 		(void) memcpy(cred->adc_fullname.name, ixdr, (uint_t)namelen);
179 		cred->adc_fullname.name[namelen] = 0;
180 		ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
181 		cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
182 		cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
183 		cred->adc_fullname.window = (uint32_t)*ixdr++;
184 		fullname_rcvd++;
185 		break;
186 	case ADN_NICKNAME:
187 		cred->adc_nickname = (uint32_t)*ixdr++;
188 		break;
189 	default:
190 		return (AUTH_BADCRED);
191 	}
192 
193 	if ((uint_t)msg->rm_call.cb_verf.oa_length == 0)
194 		return (AUTH_BADVERF);
195 	/*
196 	 * Get the verifier
197 	 */
198 	/* LINTED pointer cast */
199 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
200 	verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
201 	verf.adv_xtimestamp.key.low = (uint32_t)*ixdr++;
202 	verf.adv_int_u = (uint32_t)*ixdr++;
203 
204 	(void) mutex_lock(&authdes_lock);
205 
206 	/*
207 	 * Get the conversation key
208 	 */
209 	if (fullname_rcvd) {	/* ADN_FULLNAME */
210 		netobj	pkey;
211 		char	pkey_data[1024];
212 
213 again:
214 		init_sessionkey = cred->adc_fullname.key;
215 		sessionkey = &init_sessionkey;
216 
217 		if (!__getpublickey_cached(cred->adc_fullname.name,
218 				pkey_data, &from_cache)) {
219 			/*
220 			 * if the user has no public key, treat him as the
221 			 * unauthenticated identity - nobody. If this
222 			 * works, it means the client didn't find the
223 			 * user's keys and used nobody's secret key
224 			 * as a backup.
225 			 */
226 			if (!__getpublickey_cached("nobody",
227 						pkey_data, &from_cache)) {
228 				__msgout(LOG_INFO,
229 				"_svcauth_des: no public key for nobody or ",
230 				cred->adc_fullname.name);
231 				(void) mutex_unlock(&authdes_lock);
232 				return (AUTH_BADCRED); /* no key */
233 			}
234 
235 			/*
236 			 * found a public key for nobody. change
237 			 * the fullname id to nobody, so the caller
238 			 * thinks the client specified nobody
239 			 * as the user identity.
240 			 */
241 			(void) strcpy(cred->adc_fullname.name, "nobody");
242 		}
243 		pkey.n_bytes = pkey_data;
244 		pkey.n_len = strlen(pkey_data) + 1;
245 		if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
246 				sessionkey) < 0) {
247 			if (from_cache) {
248 				__getpublickey_flush(cred->adc_fullname.name);
249 				goto again;
250 			}
251 			__msgout(LOG_INFO,
252 			    "_svcauth_des: key_decryptsessionkey failed for",
253 			    cred->adc_fullname.name);
254 			(void) mutex_unlock(&authdes_lock);
255 			return (AUTH_BADCRED);	/* key not found */
256 		}
257 	} else { /* ADN_NICKNAME */
258 		sid = cred->adc_nickname;
259 		if (sid >= authdes_cachesz) {
260 			__msgout(LOG_INFO, "_svcauth_des:", "bad nickname");
261 			(void) mutex_unlock(&authdes_lock);
262 			return (AUTH_BADCRED);	/* garbled credential */
263 		}
264 		/* actually check that the entry is not null */
265 		entry = &_rpc_authdes_cache[sid];
266 		if (entry->rname == NULL) {
267 			(void) mutex_unlock(&authdes_lock);
268 			return (AUTH_BADCRED);	/* cached out */
269 		}
270 		sessionkey = &_rpc_authdes_cache[sid].key;
271 	}
272 
273 	/*
274 	 * Decrypt the timestamp
275 	 */
276 	cryptbuf[0] = verf.adv_xtimestamp;
277 	if (fullname_rcvd) {	/* ADN_FULLNAME */
278 		cryptbuf[1].key.high = cred->adc_fullname.window;
279 		cryptbuf[1].key.low = verf.adv_winverf;
280 		ivec.key.high = ivec.key.low = 0;
281 		status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
282 			2 * (int)sizeof (des_block), DES_DECRYPT | DES_HW,
283 			(char *)&ivec);
284 	} else {
285 		status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
286 			(int)sizeof (des_block), DES_DECRYPT | DES_HW);
287 	}
288 	if (DES_FAILED(status)) {
289 		if (fullname_rcvd && from_cache) {
290 			__getpublickey_flush(cred->adc_fullname.name);
291 			goto again;
292 		}
293 		__msgout(LOG_ERR, "_svcauth_des: DES decryption failure for",
294 			fullname_rcvd ? cred->adc_fullname.name :
295 			_rpc_authdes_cache[sid].rname);
296 		(void) mutex_unlock(&authdes_lock);
297 		return (AUTH_FAILED);	/* system error */
298 	}
299 
300 	/*
301 	 * XDR the decrypted timestamp
302 	 */
303 	ixdr = (int32_t *)cryptbuf;
304 	timestamp.tv_sec = IXDR_GET_INT32(ixdr);
305 	timestamp.tv_usec = IXDR_GET_INT32(ixdr);
306 
307 	/*
308 	 * Check for valid credentials and verifiers.
309 	 * They could be invalid because the key was flushed
310 	 * out of the cache, and so a new session should begin.
311 	 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
312 	 */
313 	{
314 		struct timeval current;
315 		int	nick;
316 		int	winverf;
317 
318 		if (fullname_rcvd) {
319 			window = IXDR_GET_U_INT32(ixdr);
320 			winverf = IXDR_GET_U_INT32(ixdr);
321 			if (winverf != window - 1) {
322 				if (from_cache) {
323 					__getpublickey_flush(
324 						cred->adc_fullname.name);
325 					goto again;
326 				}
327 				__msgout(LOG_INFO,
328 					"_svcauth_des: corrupted window from",
329 					cred->adc_fullname.name);
330 				(void) mutex_unlock(&authdes_lock);
331 				/* garbled credential or invalid secret key */
332 				return (AUTH_BADCRED);
333 			}
334 			cache_spot_id = cache_spot(sessionkey,
335 						cred->adc_fullname.name,
336 
337 					&timestamp);
338 			if (cache_spot_id < 0) {
339 			__msgout(LOG_INFO,
340 				"_svcauth_des: replayed credential from",
341 				cred->adc_fullname.name);
342 				(void) mutex_unlock(&authdes_lock);
343 				return (AUTH_REJECTEDCRED);	/* replay */
344 			} else sid = cache_spot_id;
345 			nick = 0;
346 		} else {	/* ADN_NICKNAME */
347 			window = _rpc_authdes_cache[sid].window;
348 			nick = 1;
349 		}
350 
351 		if ((ulong_t)timestamp.tv_usec >= USEC_PER_SEC) {
352 			if (fullname_rcvd && from_cache) {
353 				__getpublickey_flush(cred->adc_fullname.name);
354 				goto again;
355 			}
356 		__msgout(LOG_INFO,
357 			"_svcauth_des: invalid timestamp received from",
358 			fullname_rcvd ? cred->adc_fullname.name :
359 				_rpc_authdes_cache[sid].rname);
360 			/* cached out (bad key), or garbled verifier */
361 			(void) mutex_unlock(&authdes_lock);
362 			return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
363 		}
364 		if (nick && BEFORE(&timestamp,
365 				&_rpc_authdes_cache[sid].laststamp)) {
366 			if (fullname_rcvd && from_cache) {
367 				__getpublickey_flush(cred->adc_fullname.name);
368 				goto again;
369 			}
370 			__msgout(LOG_INFO,
371 	"_svcauth_des: timestamp is earlier than the one previously seen from",
372 			fullname_rcvd ? cred->adc_fullname.name :
373 				_rpc_authdes_cache[sid].rname);
374 			(void) mutex_unlock(&authdes_lock);
375 			return (AUTH_REJECTEDVERF);	/* replay */
376 		}
377 		(void) gettimeofday(&current, NULL);
378 		current.tv_sec -= window;	/* allow for expiration */
379 		if (!BEFORE(&current, &timestamp)) {
380 			if (fullname_rcvd && from_cache) {
381 				__getpublickey_flush(cred->adc_fullname.name);
382 				goto again;
383 			}
384 			__msgout(LOG_INFO,
385 				"_svcauth_des: timestamp expired for",
386 				fullname_rcvd ? cred->adc_fullname.name :
387 					_rpc_authdes_cache[sid].rname);
388 			/* replay, or garbled credential */
389 			(void) mutex_unlock(&authdes_lock);
390 			return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
391 		}
392 	}
393 
394 	/*
395 	 * Set up the reply verifier
396 	 */
397 	verf.adv_nickname = sid;
398 
399 	/*
400 	 * xdr the timestamp before encrypting
401 	 */
402 	ixdr = (int32_t *)cryptbuf;
403 	IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
404 	IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
405 
406 	/*
407 	 * encrypt the timestamp
408 	 */
409 	status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
410 				(int)sizeof (des_block), DES_ENCRYPT | DES_HW);
411 	if (DES_FAILED(status)) {
412 		__msgout(LOG_ERR, "_svcauth_des: DES encryption failure for",
413 			fullname_rcvd ? cred->adc_fullname.name :
414 			_rpc_authdes_cache[sid].rname);
415 		(void) mutex_unlock(&authdes_lock);
416 		return (AUTH_FAILED);	/* system error */
417 	}
418 	verf.adv_xtimestamp = cryptbuf[0];
419 
420 	/*
421 	 * Serialize the reply verifier, and update rqst
422 	 */
423 	/* LINTED pointer cast */
424 	ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
425 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
426 	*ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
427 	*ixdr++ = (int32_t)verf.adv_int_u;
428 
429 	rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
430 	rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
431 	rqst->rq_xprt->xp_verf.oa_length =
432 		(char *)ixdr - msg->rm_call.cb_verf.oa_base;
433 	if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
434 		__msgout(LOG_ERR,
435 			"_svcauth_des: Authenticator length error",
436 			fullname_rcvd ? cred->adc_fullname.name :
437 			_rpc_authdes_cache[sid].rname);
438 		(void) mutex_unlock(&authdes_lock);
439 		return (AUTH_REJECTEDVERF);
440 	}
441 
442 	/*
443 	 * We succeeded, commit the data to the cache now and
444 	 * finish cooking the credential.
445 	 */
446 	entry = &_rpc_authdes_cache[sid];
447 	entry->laststamp = timestamp;
448 	cache_ref(sid);
449 	if (cred->adc_namekind == ADN_FULLNAME) {
450 		cred->adc_fullname.window = window;
451 		cred->adc_nickname = sid;	/* save nickname */
452 		if (entry->rname != NULL)
453 			free(entry->rname);
454 		entry->rname = malloc(strlen(cred->adc_fullname.name) + 1);
455 		if (entry->rname != NULL) {
456 			(void) strcpy(entry->rname, cred->adc_fullname.name);
457 		} else {
458 			__msgout(LOG_CRIT, "_svcauth_des:", "out of memory");
459 			(void) mutex_unlock(&authdes_lock);
460 			return (AUTH_FAILED);
461 		}
462 		entry->key = *sessionkey;
463 		entry->window = window;
464 		/* mark any cached cred invalid */
465 		invalidate(entry->localcred);
466 	} else { /* ADN_NICKNAME */
467 		/*
468 		 * nicknames are cooked into fullnames
469 		 */
470 		cred->adc_namekind = ADN_FULLNAME;
471 		cred->adc_fullname.name = entry->rname;
472 		cred->adc_fullname.key = entry->key;
473 		cred->adc_fullname.window = entry->window;
474 	}
475 	(void) mutex_unlock(&authdes_lock);
476 	return (AUTH_OK);	/* we made it! */
477 }
478 
479 
480 /*
481  * Initialize the cache
482  */
483 static int
cache_init(void)484 cache_init(void)
485 {
486 	int i;
487 
488 /* LOCK HELD ON ENTRY: authdes_lock */
489 
490 	assert(MUTEX_HELD(&authdes_lock));
491 	_rpc_authdes_cache =
492 		malloc(sizeof (struct cache_entry) * authdes_cachesz);
493 	if (_rpc_authdes_cache == NULL) {
494 		__msgout(LOG_CRIT, "cache_init:", "out of memory");
495 		return (-1);
496 	}
497 	(void) memset(_rpc_authdes_cache, 0,
498 		sizeof (struct cache_entry) * authdes_cachesz);
499 
500 	/*
501 	 * Initialize the lru chain (linked-list)
502 	 */
503 	for (i = 1; i < (authdes_cachesz - 1); i++) {
504 		_rpc_authdes_cache[i].index = i;
505 		_rpc_authdes_cache[i].next = &_rpc_authdes_cache[i + 1];
506 		_rpc_authdes_cache[i].prev = &_rpc_authdes_cache[i - 1];
507 	}
508 	cache_head = &_rpc_authdes_cache[0];
509 	cache_tail = &_rpc_authdes_cache[authdes_cachesz - 1];
510 
511 	/*
512 	 * These elements of the chain need special attention...
513 	 */
514 	cache_head->index = 0;
515 	cache_tail->index = authdes_cachesz - 1;
516 	cache_head->next = &_rpc_authdes_cache[1];
517 	cache_head->prev = cache_tail;
518 	cache_tail->next = cache_head;
519 	cache_tail->prev = &_rpc_authdes_cache[authdes_cachesz - 2];
520 	return (0);
521 }
522 
523 
524 /*
525  * Find the lru victim
526  */
527 static uint32_t
cache_victim(void)528 cache_victim(void)
529 {
530 /* LOCK HELD ON ENTRY: authdes_lock */
531 
532 	assert(MUTEX_HELD(&authdes_lock));
533 	return (cache_head->index);			/* list in lru order */
534 }
535 
536 /*
537  * Note that sid was referenced
538  */
539 static void
cache_ref(uint32_t sid)540 cache_ref(uint32_t sid)
541 {
542 	struct cache_entry *curr = &_rpc_authdes_cache[sid];
543 
544 
545 /* LOCK HELD ON ENTRY: authdes_lock */
546 
547 	assert(MUTEX_HELD(&authdes_lock));
548 
549 	/*
550 	 * move referenced item from its place on the LRU chain
551 	 * to the tail of the chain while checking for special
552 	 * conditions (mainly for performance).
553 	 */
554 	if (cache_tail == curr) {			/* no work to do */
555 		/*EMPTY*/;
556 	} else if (cache_head == curr) {
557 		cache_head = cache_head->next;
558 		cache_tail = curr;
559 	} else {
560 		(curr->next)->prev = curr->prev;	/* fix thy neighbor */
561 		(curr->prev)->next = curr->next;
562 		curr->next = cache_head;		/* fix thy self... */
563 		curr->prev = cache_tail;
564 		cache_head->prev = curr;		/* fix the head  */
565 		cache_tail->next = curr;		/* fix the tail  */
566 		cache_tail = curr;			/* move the tail */
567 	}
568 }
569 
570 /*
571  * Find a spot in the cache for a credential containing
572  * the items given. Return -1 if a replay is detected, otherwise
573  * return the spot in the cache.
574  */
575 static int
cache_spot(des_block * key,char * name,struct timeval * timestamp)576 cache_spot(des_block *key, char *name, struct timeval *timestamp)
577 {
578 	struct cache_entry *cp;
579 	int i;
580 	uint32_t hi;
581 
582 /* LOCK HELD ON ENTRY: authdes_lock */
583 
584 	assert(MUTEX_HELD(&authdes_lock));
585 	hi = key->key.high;
586 	for (cp = _rpc_authdes_cache, i = 0; i < authdes_cachesz; i++, cp++) {
587 		if (cp->key.key.high == hi &&
588 		    cp->key.key.low == key->key.low &&
589 		    cp->rname != NULL &&
590 		    memcmp(cp->rname, name, strlen(name) + 1) == 0) {
591 			if (BEFORE(timestamp, &cp->laststamp)) {
592 				svcauthdes_stats.ncachereplays++;
593 				return (-1);	/* replay */
594 			}
595 			svcauthdes_stats.ncachehits++;
596 			return (i);
597 			/* refresh */
598 		}
599 	}
600 	svcauthdes_stats.ncachemisses++;
601 	return (cache_victim());
602 }
603 
604 
605 /*
606  * Local credential handling stuff.
607  * NOTE: bsd unix dependent.
608  * Other operating systems should put something else here.
609  */
610 #define	UNKNOWN 	-2	/* grouplen, if cached cred is unknown user */
611 #define	INVALID		-1 	/* grouplen, if cache entry is invalid */
612 
613 struct bsdcred {
614 	uid_t uid;		/* cached uid */
615 	gid_t gid;		/* cached gid */
616 	short grouplen;	/* length of cached groups */
617 	gid_t groups[1];	/* cached groups allocate _SC_NGROUPS_MAX */
618 };
619 
620 static void
invalidate(char * cred)621 invalidate(char *cred)
622 {
623 	if (cred == NULL)
624 		return;
625 	/* LINTED pointer cast */
626 	((struct bsdcred *)cred)->grouplen = INVALID;
627 }
628 
629 /*
630  * Map a des credential into a unix cred.
631  * We cache the credential here so the application does
632  * not have to make an rpc call every time to interpret
633  * the credential.
634  */
635 int
authdes_getucred(const struct authdes_cred * adc,uid_t * uid,gid_t * gid,short * grouplen,gid_t * groups)636 authdes_getucred(const struct authdes_cred *adc, uid_t *uid, gid_t *gid,
637     short *grouplen, gid_t *groups)
638 {
639 	uint32_t sid;
640 	int i;
641 	uid_t i_uid;
642 	gid_t i_gid;
643 	int i_grouplen;
644 	struct bsdcred *cred;
645 
646 	sid = adc->adc_nickname;
647 	if (sid >= authdes_cachesz) {
648 		__msgout2(__getucredstr, "invalid nickname");
649 		return (0);
650 	}
651 	(void) mutex_lock(&authdes_lock);
652 	/* LINTED pointer cast */
653 	cred = (struct bsdcred *)_rpc_authdes_cache[sid].localcred;
654 	if (cred == NULL) {
655 		static size_t bsdcred_sz;
656 
657 		if (bsdcred_sz == 0) {
658 			bsdcred_sz = sizeof (struct bsdcred) +
659 			    (sysconf(_SC_NGROUPS_MAX) - 1) * sizeof (gid_t);
660 		}
661 		cred = malloc(bsdcred_sz);
662 		if (cred == NULL) {
663 			__msgout2(__getucredstr, "out of memory");
664 			(void) mutex_unlock(&authdes_lock);
665 			return (0);
666 		}
667 		_rpc_authdes_cache[sid].localcred = (char *)cred;
668 		cred->grouplen = INVALID;
669 	}
670 	if (cred->grouplen == INVALID) {
671 		/*
672 		 * not in cache: lookup
673 		 */
674 		if (!netname2user(adc->adc_fullname.name, (uid_t *)&i_uid,
675 			(gid_t *)&i_gid, &i_grouplen, (gid_t *)groups)) {
676 			__msgout2(__getucredstr, "unknown netname");
677 			/* mark as lookup up, but not found */
678 			cred->grouplen = UNKNOWN;
679 			(void) mutex_unlock(&authdes_lock);
680 			return (0);
681 		}
682 		__msgout2(__getucredstr, "missed ucred cache");
683 		*uid = cred->uid = i_uid;
684 		*gid = cred->gid = i_gid;
685 		*grouplen = cred->grouplen = i_grouplen;
686 		for (i = i_grouplen - 1; i >= 0; i--) {
687 			cred->groups[i] = groups[i];
688 		}
689 		(void) mutex_unlock(&authdes_lock);
690 		return (1);
691 	}
692 	if (cred->grouplen == UNKNOWN) {
693 		/*
694 		 * Already lookup up, but no match found
695 		 */
696 		(void) mutex_unlock(&authdes_lock);
697 		return (0);
698 	}
699 
700 	/*
701 	 * cached credentials
702 	 */
703 	*uid = cred->uid;
704 	*gid = cred->gid;
705 	*grouplen = cred->grouplen;
706 	for (i = cred->grouplen - 1; i >= 0; i--) {
707 		groups[i] = cred->groups[i];
708 	}
709 	(void) mutex_unlock(&authdes_lock);
710 	return (1);
711 }
712 
713 
714 static void
__msgout(int level,const char * str,const char * strarg)715 __msgout(int level, const char *str, const char *strarg)
716 {
717 	(void) syslog(level, "%s %s", str, strarg);
718 }
719 
720 
721 static void
__msgout2(const char * str,const char * str2)722 __msgout2(const char *str, const char *str2)
723 {
724 	(void) syslog(LOG_DEBUG, "%s %s", str, str2);
725 }
726