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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 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 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 /* 38 * svcauth_des.c, server-side des authentication 39 * 40 * We insure for the service the following: 41 * (1) The timestamp microseconds do not exceed 1 million. 42 * (2) The timestamp plus the window is less than the current time. 43 * (3) The timestamp is not less than the one previously 44 * seen in the current session. 45 * 46 * It is up to the server to determine if the window size is 47 * too small. 48 */ 49 50 #include <sys/types.h> 51 #include <sys/time.h> 52 #include <sys/systm.h> 53 #include <sys/param.h> 54 #include <sys/stream.h> 55 #include <sys/stropts.h> 56 #include <sys/strsubr.h> 57 #include <sys/tiuser.h> 58 #include <sys/tihdr.h> 59 #include <sys/t_kuser.h> 60 #include <sys/t_lock.h> 61 #include <sys/debug.h> 62 #include <sys/kmem.h> 63 #include <sys/time.h> 64 #include <sys/cmn_err.h> 65 66 #include <rpc/types.h> 67 #include <rpc/xdr.h> 68 #include <rpc/auth.h> 69 #include <rpc/auth_des.h> 70 #include <rpc/rpc_msg.h> 71 #include <rpc/svc.h> 72 #include <rpc/svc_auth.h> 73 #include <rpc/clnt.h> 74 #include <rpc/des_crypt.h> 75 76 #define USEC_PER_SEC 1000000 77 #define BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */) 78 79 /* 80 * Cache of conversation keys and some other useful items. 81 * The hash table size is controled via authdes_cachesz variable. 82 * The authdes_cachesz has to be the power of 2. 83 */ 84 #define AUTHDES_CACHE_TABLE_SZ 1024 85 static int authdes_cachesz = AUTHDES_CACHE_TABLE_SZ; 86 #define HASH(key) ((key) & (authdes_cachesz - 1)) 87 88 /* low water mark for the number of cache entries */ 89 static int low_cache_entries = 128; 90 91 struct authdes_cache_entry { 92 uint32_t nickname; /* nick name id */ 93 uint32_t window; /* credential lifetime window */ 94 des_block key; /* conversation key */ 95 time_t ref_time; /* time referenced previously */ 96 char *rname; /* client's name */ 97 caddr_t localcred; /* generic local credential */ 98 struct authdes_cache_entry *prev, *next; /* hash table linked list */ 99 struct authdes_cache_entry *lru_prev, *lru_next; /* LRU linked list */ 100 kmutex_t lock; /* cache entry lock */ 101 }; 102 static struct authdes_cache_entry **authdes_cache; /* [authdes_cachesz] */ 103 static struct authdes_cache_entry *lru_first = NULL; 104 static struct authdes_cache_entry *lru_last = NULL; 105 static kmutex_t authdes_lock; /* cache table lock */ 106 107 static struct kmem_cache *authdes_cache_handle; 108 static uint32_t Nickname = 0; 109 110 static struct authdes_cache_entry *authdes_cache_new(char *, 111 des_block *, uint32_t); 112 static struct authdes_cache_entry *authdes_cache_get(uint32_t); 113 static void authdes_cache_reclaim(void *); 114 static void sweep_cache(); 115 116 /* 117 * After 12 hours, check and delete cache entries that have been 118 * idled for more than 10 hours. 119 */ 120 static time_t authdes_sweep_interval = 12*60*60; 121 static time_t authdes_cache_time = 10*60*60; 122 static time_t authdes_last_swept = 0; 123 124 /* 125 * cache statistics 126 */ 127 static int authdes_ncache = 0; /* number of current cached entries */ 128 static int authdes_ncachehits = 0; /* #times cache hit */ 129 static int authdes_ncachemisses = 0; /* #times cache missed */ 130 131 #define NOT_DEAD(ptr) ASSERT((((intptr_t)(ptr)) != 0xdeadbeef)) 132 #define IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0) 133 134 /* 135 * Service side authenticator for AUTH_DES 136 */ 137 enum auth_stat 138 _svcauth_des(struct svc_req *rqst, struct rpc_msg *msg) 139 { 140 int32_t *ixdr; 141 des_block cryptbuf[2]; 142 struct authdes_cred *cred; 143 struct authdes_verf verf; 144 int status; 145 des_block *sessionkey; 146 des_block ivec; 147 uint32_t window, winverf, namelen; 148 bool_t nick; 149 struct timeval timestamp, current_time; 150 struct authdes_cache_entry *nick_entry; 151 struct area { 152 struct authdes_cred area_cred; 153 char area_netname[MAXNETNAMELEN+1]; 154 } *area; 155 timestruc_t now; 156 157 mutex_enter(&authdes_lock); 158 if (authdes_cache == NULL) { 159 authdes_cache = kmem_zalloc( 160 sizeof (struct authdes_cache_entry *) * authdes_cachesz, 161 KM_SLEEP); 162 } 163 mutex_exit(&authdes_lock); 164 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 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 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 int groups[NGROUPS_UMAX]; /* cached groups */ 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 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 * 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 = (struct bsdcred *)kmem_alloc(sizeof (struct bsdcred), 513 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 * 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 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 mutex_destroy(&p->lock); 625 kmem_cache_free(authdes_cache_handle, p); 626 627 authdes_ncache--; 628 } 629 mutex_exit(&authdes_lock); 630 RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n", 631 authdes_ncache); 632 } 633 634 /* 635 * Walk through the LRU doubly-linked list and delete the cache 636 * entries that have not been used for more than authdes_cache_time. 637 * 638 * Caller should have locked the cache table. 639 */ 640 void 641 sweep_cache() { 642 struct authdes_cache_entry *p; 643 644 ASSERT(MUTEX_HELD(&authdes_lock)); 645 while ((p = lru_last) != lru_first) { 646 IS_ALIGNED(p); 647 NOT_DEAD(p); 648 649 /* 650 * If the last LRU entry idled less than authdes_cache_time, 651 * we are done with the sweeping. 652 */ 653 if (p->ref_time + authdes_cache_time > gethrestime_sec()) 654 break; 655 656 /* update the hash linked list */ 657 if (p->prev == NULL) { 658 authdes_cache[HASH(p->nickname)] = p->next; 659 } else { 660 p->prev->next = p->next; 661 } 662 if (p->next != NULL) { 663 p->next->prev = p->prev; 664 } 665 666 /* update the LRU linked list */ 667 p->lru_prev->lru_next = NULL; 668 lru_last = p->lru_prev; 669 670 kmem_free(p->rname, strlen(p->rname) + 1); 671 kmem_free(p->localcred, sizeof (struct bsdcred)); 672 mutex_destroy(&p->lock); 673 kmem_cache_free(authdes_cache_handle, p); 674 675 authdes_ncache--; 676 } 677 678 authdes_last_swept = gethrestime_sec(); 679 RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n", 680 authdes_ncache); 681 } 682