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