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