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 ×tamp);
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(×tamp,
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(¤t, NULL);
378 current.tv_sec -= window; /* allow for expiration */
379 if (!BEFORE(¤t, ×tamp)) {
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