Libinfo-222.4.6.tar.gz
[apple/libinfo.git] / lookup.subproj / lu_group.c
1 /*
2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Unix group lookup
26 * Copyright (C) 1989 by NeXT, Inc.
27 */
28
29 #include <stdlib.h>
30 #include <mach/mach.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <rpc/types.h>
35 #include <rpc/xdr.h>
36 #include <grp.h>
37 #include <pwd.h>
38 #include <netinet/in.h>
39 #include <sys/param.h>
40 #include <unistd.h>
41 #include <pthread.h>
42 #include <errno.h>
43 #include <servers/bootstrap.h>
44
45 #include "_lu_types.h"
46 #include "lookup.h"
47 #include "lu_utils.h"
48 #include "lu_overrides.h"
49
50 #define GROUP_CACHE_SIZE 10
51 #define DEFAULT_GROUP_CACHE_TTL 10
52
53 static pthread_mutex_t _group_cache_lock = PTHREAD_MUTEX_INITIALIZER;
54 static void *_group_cache[GROUP_CACHE_SIZE] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
55 static unsigned int _group_cache_best_before[GROUP_CACHE_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
56 static unsigned int _group_cache_index = 0;
57 static unsigned int _group_cache_ttl = DEFAULT_GROUP_CACHE_TTL;
58
59 static pthread_mutex_t _group_lock = PTHREAD_MUTEX_INITIALIZER;
60
61 /*
62 * Support for memberd calls
63 */
64 #define MEMBERD_NAME "com.apple.memberd"
65 static mach_port_t mbr_port = MACH_PORT_NULL;
66 typedef uint32_t GIDArray[16];
67 extern kern_return_t _mbr_GetGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, GIDArray gids);
68
69 #define GR_GET_NAME 1
70 #define GR_GET_GID 2
71 #define GR_GET_ENT 3
72
73 static void
74 free_group_data(struct group *g)
75 {
76 char **mem;
77
78 if (g == NULL) return;
79
80 if (g->gr_name != NULL) free(g->gr_name);
81 if (g->gr_passwd != NULL) free(g->gr_passwd);
82
83 mem = g->gr_mem;
84 if (mem != NULL)
85 {
86 while (*mem != NULL) free(*mem++);
87 free(g->gr_mem);
88 }
89 }
90
91 static void
92 free_group(struct group *g)
93 {
94 if (g == NULL) return;
95 free_group_data(g);
96 free(g);
97 }
98
99 static void
100 free_lu_thread_info_group(void *x)
101 {
102 struct lu_thread_info *tdata;
103
104 if (x == NULL) return;
105
106 tdata = (struct lu_thread_info *)x;
107
108 if (tdata->lu_entry != NULL)
109 {
110 free_group((struct group *)tdata->lu_entry);
111 tdata->lu_entry = NULL;
112 }
113
114 _lu_data_free_vm_xdr(tdata);
115
116 free(tdata);
117 }
118
119 static struct group *
120 extract_group(XDR *xdr)
121 {
122 int i, j, nkeys, nvals, status;
123 char *key, **vals;
124 struct group *g;
125
126 if (xdr == NULL) return NULL;
127
128 if (!xdr_int(xdr, &nkeys)) return NULL;
129
130 g = (struct group *)calloc(1, sizeof(struct group));
131 g->gr_gid = -2;
132
133 for (i = 0; i < nkeys; i++)
134 {
135 key = NULL;
136 vals = NULL;
137 nvals = 0;
138
139 status = _lu_xdr_attribute(xdr, &key, &vals, &nvals);
140 if (status < 0)
141 {
142 free_group(g);
143 return NULL;
144 }
145
146 if (nvals == 0)
147 {
148 free(key);
149 continue;
150 }
151
152 j = 0;
153
154 if ((g->gr_name == NULL) && (!strcmp("name", key)))
155 {
156 g->gr_name = vals[0];
157 j = 1;
158 }
159 else if ((g->gr_passwd == NULL) && (!strcmp("passwd", key)))
160 {
161 g->gr_passwd = vals[0];
162 j = 1;
163 }
164 else if ((g->gr_gid == (gid_t)-2) && (!strcmp("gid", key)))
165 {
166 g->gr_gid = atoi(vals[0]);
167 if ((g->gr_gid == 0) && (strcmp(vals[0], "0"))) g->gr_gid = -2;
168 }
169 else if ((g->gr_mem == NULL) && (!strcmp("users", key)))
170 {
171 g->gr_mem = vals;
172 j = nvals;
173 vals = NULL;
174 }
175
176 free(key);
177 if (vals != NULL)
178 {
179 for (; j < nvals; j++) free(vals[j]);
180 free(vals);
181 }
182 }
183
184 if (g->gr_name == NULL) g->gr_name = strdup("");
185 if (g->gr_passwd == NULL) g->gr_passwd = strdup("");
186 if (g->gr_mem == NULL) g->gr_mem = (char **)calloc(1, sizeof(char *));
187
188 return g;
189 }
190
191 static struct group *
192 copy_group(struct group *in)
193 {
194 struct group *g;
195 int i, len;
196
197 if (in == NULL) return NULL;
198
199 g = (struct group *)calloc(1, sizeof(struct group));
200
201 g->gr_name = LU_COPY_STRING(in->gr_name);
202 g->gr_passwd = LU_COPY_STRING(in->gr_passwd);
203 g->gr_gid = in->gr_gid;
204
205 len = 0;
206 if (in->gr_mem != NULL)
207 {
208 for (len = 0; in->gr_mem[len] != NULL; len++);
209 }
210
211 g->gr_mem = (char **)calloc(len + 1, sizeof(char *));
212 for (i = 0; i < len; i++)
213 {
214 g->gr_mem[i] = strdup(in->gr_mem[i]);
215 }
216
217 return g;
218 }
219
220 static int
221 copy_group_r(struct group *in, struct group *out, char *buffer, int buflen)
222 {
223 int i, len, hsize;
224 unsigned long addr;
225 char *bp, *ap;
226
227 if (in == NULL) return -1;
228 if (out == NULL) return -1;
229
230 if (buffer == NULL) buflen = 0;
231
232 /* Calculate size of input */
233 hsize = 0;
234 if (in->gr_name != NULL) hsize += (strlen(in->gr_name) + 1);
235 if (in->gr_passwd != NULL) hsize += (strlen(in->gr_passwd) + 1);
236
237 /* NULL pointer at end of list */
238 hsize += sizeof(char *);
239
240 len = 0;
241 if (in->gr_mem != NULL)
242 {
243 for (len = 0; in->gr_mem[len] != NULL; len++)
244 {
245 hsize += sizeof(char *);
246 hsize += (strlen(in->gr_mem[len]) + 1);
247 }
248 }
249
250 /* Check buffer space */
251 if (hsize > buflen) return -1;
252
253 /* Copy result into caller's struct group, using buffer for memory */
254 bp = buffer;
255
256 out->gr_name = NULL;
257 if (in->gr_name != NULL)
258 {
259 out->gr_name = bp;
260 hsize = strlen(in->gr_name) + 1;
261 memmove(bp, in->gr_name, hsize);
262 bp += hsize;
263 }
264
265 out->gr_passwd = NULL;
266 if (in->gr_passwd != NULL)
267 {
268 out->gr_passwd = bp;
269 hsize = strlen(in->gr_passwd) + 1;
270 memmove(bp, in->gr_passwd, hsize);
271 bp += hsize;
272 }
273
274 out->gr_gid = in->gr_gid;
275
276 out->gr_mem = NULL;
277 ap = bp + ((len + 1) * sizeof(char *));
278
279 if (in->gr_mem != NULL)
280 {
281 out->gr_mem = (char **)bp;
282 for (i = 0; i < len; i++)
283 {
284 addr = (unsigned long)ap;
285 memmove(bp, &addr, sizeof(unsigned long));
286 bp += sizeof(unsigned long);
287
288 hsize = strlen(in->gr_mem[i]) + 1;
289 memmove(ap, in->gr_mem[i], hsize);
290 ap += hsize;
291 }
292 }
293
294 memset(bp, 0, sizeof(unsigned long));
295 bp = ap;
296
297 return 0;
298 }
299
300 static void
301 recycle_group(struct lu_thread_info *tdata, struct group *in)
302 {
303 struct group *g;
304
305 if (tdata == NULL) return;
306 g = (struct group *)tdata->lu_entry;
307
308 if (in == NULL)
309 {
310 free_group(g);
311 tdata->lu_entry = NULL;
312 }
313
314 if (tdata->lu_entry == NULL)
315 {
316 tdata->lu_entry = in;
317 return;
318 }
319
320 free_group_data(g);
321
322 g->gr_name = in->gr_name;
323 g->gr_passwd = in->gr_passwd;
324 g->gr_gid = in->gr_gid;
325 g->gr_mem = in->gr_mem;
326
327 free(in);
328 }
329
330 __private_extern__ unsigned int
331 get_group_cache_ttl()
332 {
333 return _group_cache_ttl;
334 }
335
336 __private_extern__ void
337 set_group_cache_ttl(unsigned int ttl)
338 {
339 int i;
340
341 pthread_mutex_lock(&_group_cache_lock);
342
343 _group_cache_ttl = ttl;
344
345 if (ttl == 0)
346 {
347 for (i = 0; i < GROUP_CACHE_SIZE; i++)
348 {
349 if (_group_cache[i] == NULL) continue;
350
351 free_group((struct group *)_group_cache[i]);
352 _group_cache[i] = NULL;
353 _group_cache_best_before[i] = 0;
354 }
355 }
356
357 pthread_mutex_unlock(&_group_cache_lock);
358 }
359
360 static void
361 cache_group(struct group *gr)
362 {
363 struct timeval now;
364 struct group *grcache;
365
366 if (_group_cache_ttl == 0) return;
367 if (gr == NULL) return;
368
369 pthread_mutex_lock(&_group_cache_lock);
370
371 grcache = copy_group(gr);
372
373 gettimeofday(&now, NULL);
374
375 if (_group_cache[_group_cache_index] != NULL)
376 free_group((struct group *)_group_cache[_group_cache_index]);
377
378 _group_cache[_group_cache_index] = grcache;
379 _group_cache_best_before[_group_cache_index] = now.tv_sec + _group_cache_ttl;
380 _group_cache_index = (_group_cache_index + 1) % GROUP_CACHE_SIZE;
381
382 pthread_mutex_unlock(&_group_cache_lock);
383 }
384
385 static struct group *
386 cache_getgrnam(const char *name)
387 {
388 int i;
389 struct group *gr, *res;
390 struct timeval now;
391
392 if (_group_cache_ttl == 0) return NULL;
393 if (name == NULL) return NULL;
394
395 pthread_mutex_lock(&_group_cache_lock);
396
397 gettimeofday(&now, NULL);
398
399 for (i = 0; i < GROUP_CACHE_SIZE; i++)
400 {
401 if (_group_cache_best_before[i] == 0) continue;
402 if ((unsigned int)now.tv_sec > _group_cache_best_before[i]) continue;
403
404 gr = (struct group *)_group_cache[i];
405
406 if (gr->gr_name == NULL) continue;
407
408 if (!strcmp(name, gr->gr_name))
409 {
410 res = copy_group(gr);
411 pthread_mutex_unlock(&_group_cache_lock);
412 return res;
413 }
414 }
415
416 pthread_mutex_unlock(&_group_cache_lock);
417 return NULL;
418 }
419
420 static struct group *
421 cache_getgrgid(int gid)
422 {
423 int i;
424 struct group *gr, *res;
425 struct timeval now;
426
427 if (_group_cache_ttl == 0) return NULL;
428
429 pthread_mutex_lock(&_group_cache_lock);
430
431 gettimeofday(&now, NULL);
432
433 for (i = 0; i < GROUP_CACHE_SIZE; i++)
434 {
435 if (_group_cache_best_before[i] == 0) continue;
436 if ((unsigned int)now.tv_sec > _group_cache_best_before[i]) continue;
437
438 gr = (struct group *)_group_cache[i];
439
440 if ((gid_t)gid == gr->gr_gid)
441 {
442 res = copy_group(gr);
443 pthread_mutex_unlock(&_group_cache_lock);
444 return res;
445 }
446 }
447
448 pthread_mutex_unlock(&_group_cache_lock);
449 return NULL;
450 }
451
452 static struct group *
453 lu_getgrgid(int gid)
454 {
455 struct group *g;
456 unsigned int datalen;
457 XDR inxdr;
458 static int proc = -1;
459 int count;
460 char *lookup_buf;
461
462 if (proc < 0)
463 {
464 if (_lookup_link(_lu_port, "getgrgid", &proc) != KERN_SUCCESS)
465 {
466 return NULL;
467 }
468 }
469
470 gid = htonl(gid);
471 datalen = 0;
472 lookup_buf = NULL;
473
474 if (_lookup_all(_lu_port, proc, (unit *)&gid, 1, &lookup_buf, &datalen) != KERN_SUCCESS)
475 {
476 return NULL;
477 }
478
479 datalen *= BYTES_PER_XDR_UNIT;
480 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
481
482 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
483
484 count = 0;
485 if (!xdr_int(&inxdr, &count))
486 {
487 xdr_destroy(&inxdr);
488 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
489 return NULL;
490 }
491
492 if (count == 0)
493 {
494 xdr_destroy(&inxdr);
495 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
496 return NULL;
497 }
498
499 g = extract_group(&inxdr);
500 xdr_destroy(&inxdr);
501 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
502
503 return g;
504 }
505
506 static struct group *
507 lu_getgrnam(const char *name)
508 {
509 struct group *g;
510 unsigned int datalen;
511 char namebuf[_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT];
512 XDR outxdr;
513 XDR inxdr;
514 static int proc = -1;
515 int count;
516 char *lookup_buf;
517
518 if (proc < 0)
519 {
520 if (_lookup_link(_lu_port, "getgrnam", &proc) != KERN_SUCCESS)
521 {
522 return NULL;
523 }
524 }
525
526 xdrmem_create(&outxdr, namebuf, sizeof(namebuf), XDR_ENCODE);
527
528 if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
529 {
530 xdr_destroy(&outxdr);
531 return NULL;
532 }
533
534 datalen = 0;
535 lookup_buf = NULL;
536
537 if (_lookup_all(_lu_port, proc, (unit *)namebuf, xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen) != KERN_SUCCESS)
538 {
539 return NULL;
540 }
541
542 xdr_destroy(&outxdr);
543
544 datalen *= BYTES_PER_XDR_UNIT;
545 if ((lookup_buf == NULL) || (datalen == 0)) return NULL;
546
547 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
548
549 count = 0;
550 if (!xdr_int(&inxdr, &count))
551 {
552 xdr_destroy(&inxdr);
553 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
554 return NULL;
555 }
556
557 if (count == 0)
558 {
559 xdr_destroy(&inxdr);
560 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
561 return NULL;
562 }
563
564 g = extract_group(&inxdr);
565 xdr_destroy(&inxdr);
566 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
567
568 return g;
569 }
570
571 /*
572 * add a group to a list
573 *
574 * if dupok is non-zero, it's OK to add a duplicate entry
575 * if dupok is zero, we only add the gid if it is new
576 * (*listcount) is incremented if the gid was added
577 * returns -1 if adding the gid would overflow the list
578 *
579 */
580 static int
581 _add_group(int gid, int *list, int *listcount, int max, int dupok, int laststatus)
582 {
583 int i, n, addit, status;
584
585 if (laststatus != 0) return laststatus;
586
587 status = 0;
588 addit = 1;
589 n = *listcount;
590
591 if (dupok == 0)
592 {
593 for (i = 0; (i < n) && (addit == 1); i++)
594 {
595 if (list[i] == gid) addit = 0;
596 }
597 }
598
599 if (addit == 0) return 0;
600 if (n >= max) return -1;
601
602 list[n] = gid;
603 *listcount = n + 1;
604 return 0;
605 }
606
607 int
608 _old_getgrouplist(const char *uname, int basegid, int *groups, int *grpcnt)
609 {
610 struct group *grp;
611 int i, status, maxgroups;
612
613 status = 0;
614 maxgroups = *grpcnt;
615 *grpcnt = 0;
616
617 /*
618 * When installing primary group, duplicate it;
619 * the first element of groups is the effective gid
620 * and will be overwritten when a setgid file is executed.
621 */
622 status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
623 status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
624
625 /*
626 * Scan the group file to find additional groups.
627 */
628 setgrent();
629
630 while ((grp = getgrent()))
631 {
632 if (grp->gr_gid == (gid_t)basegid) continue;
633 for (i = 0; grp->gr_mem[i]; i++)
634 {
635 if (!strcmp(grp->gr_mem[i], uname))
636 {
637 status = _add_group(grp->gr_gid, groups, grpcnt, maxgroups, 0, status);
638 break;
639 }
640 }
641 }
642
643 endgrent();
644 return status;
645 }
646
647 static int
648 _mbr_running()
649 {
650 kern_return_t status;
651
652 status = bootstrap_look_up(bootstrap_port, MEMBERD_NAME, &mbr_port);
653 if (status != KERN_SUCCESS) return 0;
654 if (mbr_port == MACH_PORT_NULL) return 0;
655 return 1;
656 }
657
658 /*
659 * Guess at the size of a password buffer for getpwnam_r
660 * pw_name can be MAXLOGNAME + 1 256 - sys/param.h
661 * pw_passwd can be _PASSWORD_LEN + 1 129 - pwd.h
662 * pw_dir can be MAXPATHLEN + 1 1025 - sys/syslimits.h
663 * pw_shell can be MAXPATHLEN + 1025 - sys/syslimits.h
664 * We allow pw_class and pw_gecos to take a maximum of 4098 bytes (there's no limit on these).
665 * This adds to 6533 bytes (until one of the constants changes)
666 */
667 #define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098)
668 static int
669 mbr_getgrouplist(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
670 {
671 struct passwd p, *res;
672 char buf[MAXPWBUF];
673 kern_return_t kstatus;
674 uint32_t i, count;
675 int pwstatus;
676 GIDArray gids;
677 int status, maxgroups;
678
679 status = 0;
680
681 if (mbr_port == MACH_PORT_NULL) return status;
682 if (name == NULL) return status;
683 if (groups == NULL) return status;
684 if (grpcnt == NULL) return status;
685
686 maxgroups = *grpcnt;
687 *grpcnt = 0;
688
689 status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
690 if (dupbase != 0) status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
691
692 if (status != 0) return status;
693
694 memset(&p, 0, sizeof(struct passwd));
695 memset(buf, 0, sizeof(buf));
696 res = NULL;
697
698 pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
699 if (pwstatus != 0) return status;
700 if (res == NULL) return status;
701
702 count = 0;
703 kstatus = _mbr_GetGroups(mbr_port, p.pw_uid, &count, gids);
704 if (kstatus != KERN_SUCCESS) return status;
705
706 for (i = 0; (i < count) && (status == 0); i++)
707 {
708 status = _add_group(gids[i], groups, grpcnt, maxgroups, 0, status);
709 }
710
711 return status;
712 }
713
714 static int
715 lu_getgrouplist(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
716 {
717 unsigned int datalen;
718 XDR outxdr;
719 XDR inxdr;
720 static int proc = -1;
721 char *lookup_buf;
722 char namebuf[_LU_MAXLUSTRLEN + BYTES_PER_XDR_UNIT];
723 int gid;
724 int i, count;
725 int status, maxgroups;
726
727 status = 0;
728
729 if (name == NULL) return status;
730 if (groups == NULL) return status;
731 if (grpcnt == NULL) return status;
732
733 maxgroups = *grpcnt;
734 *grpcnt = 0;
735
736 status = _add_group(basegid, groups, grpcnt, maxgroups, 0, status);
737 if (dupbase != 0) status = _add_group(basegid, groups, grpcnt, maxgroups, 1, status);
738
739 if (status != 0) return status;
740
741 if (proc < 0)
742 {
743 if (_lookup_link(_lu_port, "initgroups", &proc) != KERN_SUCCESS) return status;
744 }
745
746 xdrmem_create(&outxdr, namebuf, sizeof(namebuf), XDR_ENCODE);
747 if (!xdr__lu_string(&outxdr, (_lu_string *)&name))
748 {
749 xdr_destroy(&outxdr);
750 return status;
751 }
752
753 datalen = 0;
754 lookup_buf = NULL;
755
756 if (_lookup_all(_lu_port, proc, (unit *)namebuf, xdr_getpos(&outxdr) / BYTES_PER_XDR_UNIT, &lookup_buf, &datalen) != KERN_SUCCESS)
757 {
758 xdr_destroy(&outxdr);
759 return status;
760 }
761
762 xdr_destroy(&outxdr);
763
764 datalen *= BYTES_PER_XDR_UNIT;
765 if ((lookup_buf == NULL) || (datalen == 0)) return 0;
766
767 xdrmem_create(&inxdr, lookup_buf, datalen, XDR_DECODE);
768
769 if (!xdr_int(&inxdr, &count))
770 {
771 xdr_destroy(&inxdr);
772 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
773 return status;
774 }
775
776 for (i = 0; (i < count) && (status == 0); i++)
777 {
778 if (!xdr_int(&inxdr, &gid)) break;
779 status = _add_group(gid, groups, grpcnt, maxgroups, 0, status);
780 }
781
782 xdr_destroy(&inxdr);
783 vm_deallocate(mach_task_self(), (vm_address_t)lookup_buf, datalen);
784
785 return status;
786 }
787
788 static int
789 getgrouplist_internal(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
790 {
791 if (_mbr_running())
792 {
793 return mbr_getgrouplist(name, basegid, groups, grpcnt, dupbase);
794 }
795
796 if (_lu_running())
797 {
798 return lu_getgrouplist(name, basegid, groups, grpcnt, dupbase);
799 }
800
801 return _old_getgrouplist(name, basegid, groups, grpcnt);
802 }
803
804 int
805 getgrouplist(const char *uname, int agroup, int *groups, int *grpcnt)
806 {
807 return getgrouplist_internal(uname, agroup, groups, grpcnt, 1);
808 }
809
810 static void
811 lu_endgrent(void)
812 {
813 struct lu_thread_info *tdata;
814
815 tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
816 _lu_data_free_vm_xdr(tdata);
817 }
818
819 static int
820 lu_setgrent(void)
821 {
822 lu_endgrent();
823 return 1;
824 }
825
826 static struct group *
827 lu_getgrent()
828 {
829 struct group *g;
830 static int proc = -1;
831 struct lu_thread_info *tdata;
832
833 tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
834 if (tdata == NULL)
835 {
836 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
837 _lu_data_set_key(_lu_data_key_group, tdata);
838 }
839
840 if (tdata->lu_vm == NULL)
841 {
842 if (proc < 0)
843 {
844 if (_lookup_link(_lu_port, "getgrent", &proc) != KERN_SUCCESS)
845 {
846 lu_endgrent();
847 return NULL;
848 }
849 }
850
851 if (_lookup_all(_lu_port, proc, NULL, 0, &(tdata->lu_vm), &(tdata->lu_vm_length)) != KERN_SUCCESS)
852 {
853 lu_endgrent();
854 return NULL;
855 }
856
857 /* mig stubs measure size in words (4 bytes) */
858 tdata->lu_vm_length *= 4;
859
860 if (tdata->lu_xdr != NULL)
861 {
862 xdr_destroy(tdata->lu_xdr);
863 free(tdata->lu_xdr);
864 }
865 tdata->lu_xdr = (XDR *)calloc(1, sizeof(XDR));
866
867 xdrmem_create(tdata->lu_xdr, tdata->lu_vm, tdata->lu_vm_length, XDR_DECODE);
868 if (!xdr_int(tdata->lu_xdr, &tdata->lu_vm_cursor))
869 {
870 lu_endgrent();
871 return NULL;
872 }
873 }
874
875 if (tdata->lu_vm_cursor == 0)
876 {
877 lu_endgrent();
878 return NULL;
879 }
880
881 g = extract_group(tdata->lu_xdr);
882 if (g == NULL)
883 {
884 lu_endgrent();
885 return NULL;
886 }
887
888 tdata->lu_vm_cursor--;
889
890 return g;
891 }
892
893 static struct group *
894 getgr_internal(const char *name, gid_t gid, int source)
895 {
896 struct group *res = NULL;
897 int from_cache;
898
899 from_cache = 0;
900 res = NULL;
901
902 switch (source)
903 {
904 case GR_GET_NAME:
905 res = cache_getgrnam(name);
906 break;
907 case GR_GET_GID:
908 res = cache_getgrgid(gid);
909 break;
910 default: res = NULL;
911 }
912
913 if (res != NULL)
914 {
915 from_cache = 1;
916 }
917 else if (_lu_running())
918 {
919 switch (source)
920 {
921 case GR_GET_NAME:
922 res = lu_getgrnam(name);
923 break;
924 case GR_GET_GID:
925 res = lu_getgrgid(gid);
926 break;
927 case GR_GET_ENT:
928 res = lu_getgrent();
929 break;
930 default: res = NULL;
931 }
932 }
933 else
934 {
935 pthread_mutex_lock(&_group_lock);
936 switch (source)
937 {
938 case GR_GET_NAME:
939 res = copy_group(_old_getgrnam(name));
940 break;
941 case GR_GET_GID:
942 res = copy_group(_old_getgrgid(gid));
943 break;
944 case GR_GET_ENT:
945 res = copy_group(_old_getgrent());
946 break;
947 default: res = NULL;
948 }
949 pthread_mutex_unlock(&_group_lock);
950 }
951
952 if (from_cache == 0) cache_group(res);
953
954 return res;
955 }
956
957 static struct group *
958 getgr(const char *name, gid_t gid, int source)
959 {
960 struct group *res = NULL;
961 struct lu_thread_info *tdata;
962
963 tdata = _lu_data_create_key(_lu_data_key_group, free_lu_thread_info_group);
964 if (tdata == NULL)
965 {
966 tdata = (struct lu_thread_info *)calloc(1, sizeof(struct lu_thread_info));
967 _lu_data_set_key(_lu_data_key_group, tdata);
968 }
969
970 res = getgr_internal(name, gid, source);
971
972 recycle_group(tdata, res);
973 return (struct group *)tdata->lu_entry;
974 }
975
976 static int
977 getgr_r(const char *name, gid_t gid, int source, struct group *grp, char *buffer, size_t bufsize, struct group **result)
978 {
979 struct group *res = NULL;
980 int status;
981
982 *result = NULL;
983 errno = 0;
984
985 res = getgr_internal(name, gid, source);
986 if (res == NULL) return -1;
987
988 status = copy_group_r(res, grp, buffer, bufsize);
989 free_group(res);
990
991 if (status != 0)
992 {
993 errno = ERANGE;
994 return -1;
995 }
996
997 *result = grp;
998 return 0;
999 }
1000
1001 int
1002 initgroups(const char *name, int basegid)
1003 {
1004 int status, ngroups, groups[NGROUPS];
1005
1006 ngroups = NGROUPS;
1007
1008 status = getgrouplist_internal(name, basegid, groups, &ngroups, 0);
1009 if (status < 0) return status;
1010
1011 return setgroups(ngroups, groups);
1012 }
1013
1014 struct group *
1015 getgrnam(const char *name)
1016 {
1017 return getgr(name, -2, GR_GET_NAME);
1018 }
1019
1020 struct group *
1021 getgrgid(gid_t gid)
1022 {
1023 return getgr(NULL, gid, GR_GET_GID);
1024 }
1025
1026 struct group *
1027 getgrent(void)
1028 {
1029 return getgr(NULL, -2, GR_GET_ENT);
1030 }
1031
1032 int
1033 setgrent(void)
1034 {
1035 if (_lu_running()) lu_setgrent();
1036 else _old_setgrent();
1037 return 1;
1038 }
1039
1040 void
1041 endgrent(void)
1042 {
1043 if (_lu_running()) lu_endgrent();
1044 else _old_endgrent();
1045 }
1046
1047 int
1048 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result)
1049 {
1050 return getgr_r(name, -2, GR_GET_NAME, grp, buffer, bufsize, result);
1051 }
1052
1053 int
1054 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result)
1055 {
1056 return getgr_r(NULL, gid, GR_GET_GID, grp, buffer, bufsize, result);
1057 }