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
__svcauth_des(struct svc_req * rqst,struct rpc_msg * msg)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
cache_init(void)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
cache_victim(void)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
cache_ref(uint32_t sid)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
cache_spot(des_block * key,char * name,struct timeval * timestamp)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
invalidate(char * cred)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
authdes_getucred(const struct authdes_cred * adc,uid_t * uid,gid_t * gid,short * grouplen,gid_t * groups)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
__msgout(int level,const char * str,const char * strarg)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
__msgout2(const char * str,const char * str2)723 __msgout2(const char *str, const char *str2)
724 {
725 (void) syslog(LOG_DEBUG, "%s %s", str, str2);
726 }
727