Libinfo-278.0.3.tar.gz
[apple/libinfo.git] / lookup.subproj / lu_group.c
1 /*
2 * Copyright (c) 1999-2007 Apple 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 <pwd.h>
35 #include <netinet/in.h>
36 #include <sys/param.h>
37 #include <unistd.h>
38 #include <pthread.h>
39 #include <errno.h>
40 #include <servers/bootstrap.h>
41 #include <sys/syscall.h>
42 #include "lu_utils.h"
43 #include "lu_overrides.h"
44
45 #define ENTRY_SIZE sizeof(struct group)
46 #define ENTRY_KEY _li_data_key_group
47 #define GROUP_CACHE_SIZE 10
48
49 static pthread_mutex_t _group_cache_lock = PTHREAD_MUTEX_INITIALIZER;
50 static void *_group_cache[GROUP_CACHE_SIZE] = { NULL };
51 static unsigned int _group_cache_index = 0;
52 static unsigned int _group_cache_init = 0;
53
54 static pthread_mutex_t _group_lock = PTHREAD_MUTEX_INITIALIZER;
55
56 /*
57 * Note that we don't include grp.h and define struct group privately in here
58 * to avoid needing to produce a variant version of setgrent, which changed
59 * for UXIX03 conformance.
60 */
61 struct group
62 {
63 char *gr_name;
64 char *gr_passwd;
65 gid_t gr_gid;
66 char **gr_mem;
67 };
68
69 /* forward */
70 int setgrent(void);
71 struct group *getgrent(void);
72 void endgrent(void);
73
74 /*
75 * Support for memberd calls
76 */
77 #define MEMBERD_NAME "com.apple.memberd"
78 typedef uint32_t GIDArray[16];
79 extern kern_return_t memberdDSmig_GetGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, GIDArray gids, audit_token_t *token);
80 extern kern_return_t memberdDSmig_GetAllGroups(mach_port_t server, uint32_t uid, uint32_t *numGroups, gid_t **gids, uint32_t *gidsCnt, audit_token_t *token);
81 __private_extern__ uid_t audit_token_uid(audit_token_t a);
82
83 #define GR_GET_NAME 1
84 #define GR_GET_GID 2
85 #define GR_GET_ENT 3
86
87 static struct group *
88 copy_group(struct group *in)
89 {
90 if (in == NULL) return NULL;
91
92 return (struct group *)LI_ils_create("ss4*", in->gr_name, in->gr_passwd, in->gr_gid, in->gr_mem);
93 }
94
95 /*
96 * Extract the next group entry from a kvarray.
97 */
98 static void *
99 extract_group(kvarray_t *in)
100 {
101 struct group tmp;
102 uint32_t d, k, kcount;
103 char *empty[1];
104
105 if (in == NULL) return NULL;
106
107 d = in->curr;
108 in->curr++;
109
110 if (d >= in->count) return NULL;
111
112 empty[0] = NULL;
113 memset(&tmp, 0, ENTRY_SIZE);
114
115 tmp.gr_gid = -2;
116
117 kcount = in->dict[d].kcount;
118
119 for (k = 0; k < kcount; k++)
120 {
121 if (!strcmp(in->dict[d].key[k], "gr_name"))
122 {
123 if (tmp.gr_name != NULL) continue;
124 if (in->dict[d].vcount[k] == 0) continue;
125
126 tmp.gr_name = (char *)in->dict[d].val[k][0];
127 }
128 else if (!strcmp(in->dict[d].key[k], "gr_passwd"))
129 {
130 if (tmp.gr_passwd != NULL) continue;
131 if (in->dict[d].vcount[k] == 0) continue;
132
133 tmp.gr_passwd = (char *)in->dict[d].val[k][0];
134 }
135 else if (!strcmp(in->dict[d].key[k], "gr_gid"))
136 {
137 if (in->dict[d].vcount[k] == 0) continue;
138 tmp.gr_gid = atoi(in->dict[d].val[k][0]);
139 }
140 else if (!strcmp(in->dict[d].key[k], "gr_mem"))
141 {
142 if (tmp.gr_mem != NULL) continue;
143 if (in->dict[d].vcount[k] == 0) continue;
144
145 tmp.gr_mem = (char **)in->dict[d].val[k];
146 }
147 }
148
149 if (tmp.gr_name == NULL) tmp.gr_name = "";
150 if (tmp.gr_passwd == NULL) tmp.gr_passwd = "";
151 if (tmp.gr_mem == NULL) tmp.gr_mem = empty;
152
153 return copy_group(&tmp);
154 }
155
156 static int
157 copy_group_r(struct group *in, struct group *out, char *buffer, int buflen)
158 {
159 int i, len, hsize;
160 unsigned long addr;
161 char *bp, *ap;
162
163 if (in == NULL) return -1;
164 if (out == NULL) return -1;
165
166 if (buffer == NULL) buflen = 0;
167
168 /* Calculate size of input */
169 hsize = 0;
170 if (in->gr_name != NULL) hsize += (strlen(in->gr_name) + 1);
171 if (in->gr_passwd != NULL) hsize += (strlen(in->gr_passwd) + 1);
172
173 /* NULL pointer at end of list */
174 hsize += sizeof(char *);
175
176 len = 0;
177 if (in->gr_mem != NULL)
178 {
179 for (len = 0; in->gr_mem[len] != NULL; len++)
180 {
181 hsize += sizeof(char *);
182 hsize += (strlen(in->gr_mem[len]) + 1);
183 }
184 }
185
186 /* Check buffer space */
187 if (hsize > buflen) return -1;
188
189 /* Copy result into caller's struct group, using buffer for memory */
190 bp = buffer;
191
192 out->gr_name = NULL;
193 if (in->gr_name != NULL)
194 {
195 out->gr_name = bp;
196 hsize = strlen(in->gr_name) + 1;
197 memmove(bp, in->gr_name, hsize);
198 bp += hsize;
199 }
200
201 out->gr_passwd = NULL;
202 if (in->gr_passwd != NULL)
203 {
204 out->gr_passwd = bp;
205 hsize = strlen(in->gr_passwd) + 1;
206 memmove(bp, in->gr_passwd, hsize);
207 bp += hsize;
208 }
209
210 out->gr_gid = in->gr_gid;
211
212 out->gr_mem = NULL;
213 ap = bp + ((len + 1) * sizeof(char *));
214
215 if (in->gr_mem != NULL)
216 {
217 out->gr_mem = (char **)bp;
218 for (i = 0; i < len; i++)
219 {
220 addr = (unsigned long)ap;
221 memmove(bp, &addr, sizeof(unsigned long));
222 bp += sizeof(unsigned long);
223
224 hsize = strlen(in->gr_mem[i]) + 1;
225 memmove(ap, in->gr_mem[i], hsize);
226 ap += hsize;
227 }
228 }
229
230 memset(bp, 0, sizeof(unsigned long));
231 bp = ap;
232
233 return 0;
234 }
235
236 static void
237 cache_group(struct group *gr)
238 {
239 struct group *grcache;
240
241 if (gr == NULL) return;
242
243 pthread_mutex_lock(&_group_cache_lock);
244
245 grcache = copy_group(gr);
246
247 if (_group_cache[_group_cache_index] != NULL) LI_ils_free(_group_cache[_group_cache_index], ENTRY_SIZE);
248
249 _group_cache[_group_cache_index] = grcache;
250 _group_cache_index = (_group_cache_index + 1) % GROUP_CACHE_SIZE;
251
252 _group_cache_init = 1;
253
254 pthread_mutex_unlock(&_group_cache_lock);
255 }
256
257 static int
258 group_cache_check()
259 {
260 uint32_t i, status;
261
262 /* don't consult cache if it has not been initialized */
263 if (_group_cache_init == 0) return 1;
264
265 status = LI_L1_cache_check(ENTRY_KEY);
266
267 /* don't consult cache if it is disabled or if we can't validate */
268 if ((status == LI_L1_CACHE_DISABLED) || (status == LI_L1_CACHE_FAILED)) return 1;
269
270 /* return 0 if cache is OK */
271 if (status == LI_L1_CACHE_OK) return 0;
272
273 /* flush cache */
274 pthread_mutex_lock(&_group_cache_lock);
275
276 for (i = 0; i < GROUP_CACHE_SIZE; i++)
277 {
278 LI_ils_free(_group_cache[i], ENTRY_SIZE);
279 _group_cache[i] = NULL;
280 }
281
282 _group_cache_index = 0;
283
284 pthread_mutex_unlock(&_group_cache_lock);
285
286 /* don't consult cache - it's now empty */
287 return 1;
288 }
289
290
291 static struct group *
292 cache_getgrnam(const char *name)
293 {
294 int i;
295 struct group *gr, *res;
296
297 if (name == NULL) return NULL;
298 if (group_cache_check() != 0) return NULL;
299
300 pthread_mutex_lock(&_group_cache_lock);
301
302 for (i = 0; i < GROUP_CACHE_SIZE; i++)
303 {
304 gr = (struct group *)_group_cache[i];
305 if (gr == NULL) continue;
306
307 if (gr->gr_name == NULL) continue;
308
309 if (!strcmp(name, gr->gr_name))
310 {
311 res = copy_group(gr);
312 pthread_mutex_unlock(&_group_cache_lock);
313 return res;
314 }
315 }
316
317 pthread_mutex_unlock(&_group_cache_lock);
318 return NULL;
319 }
320
321 static struct group *
322 cache_getgrgid(int gid)
323 {
324 int i;
325 struct group *gr, *res;
326
327 if (group_cache_check() != 0) return NULL;
328
329 pthread_mutex_lock(&_group_cache_lock);
330
331 for (i = 0; i < GROUP_CACHE_SIZE; i++)
332 {
333 gr = (struct group *)_group_cache[i];
334 if (gr == NULL) continue;
335
336 if ((gid_t)gid == gr->gr_gid)
337 {
338 res = copy_group(gr);
339 pthread_mutex_unlock(&_group_cache_lock);
340 return res;
341 }
342 }
343
344 pthread_mutex_unlock(&_group_cache_lock);
345 return NULL;
346 }
347
348 static struct group *
349 ds_getgrgid(int gid)
350 {
351 static int proc = -1;
352 char val[16];
353
354 snprintf(val, sizeof(val), "%d", gid);
355 return (struct group *)LI_getone("getgrgid", &proc, extract_group, "gid", val);
356 }
357
358 static struct group *
359 ds_getgrnam(const char *name)
360 {
361 static int proc = -1;
362
363 return (struct group *)LI_getone("getgrnam", &proc, extract_group, "name", name);
364 }
365
366 /*
367 * add a group to a list
368 *
369 * if dupok is non-zero, it's OK to add a duplicate entry
370 * if dupok is zero, we only add the gid if it is new
371 * (*listcount) is incremented if the gid was added
372 * returns -1 if adding the gid would overflow the list
373 *
374 */
375 static void
376 _add_group(gid_t g, gid_t **list, uint32_t *count, int dupok)
377 {
378 uint32_t i, n, addit;
379
380 addit = 1;
381
382 if (list == NULL) return;
383 if (*list == NULL) *count = 0;
384
385 n = *count;
386
387 if (dupok == 0)
388 {
389 for (i = 0; (i < n) && (addit == 1); i++)
390 {
391 if ((*list)[i] == g) addit = 0;
392 }
393 }
394
395 if (addit == 0) return;
396
397 if (*list == NULL) *list = (gid_t *)calloc(1, sizeof(gid_t));
398 else *list = (gid_t *)realloc(*list, (n + 1) * sizeof(gid_t));
399
400 if (*list == NULL)
401 {
402 *count = 0;
403 return;
404 }
405
406 (*list)[n] = g;
407 *count = n + 1;
408 }
409
410 int
411 _old_getgrouplist(const char *uname, int basegid, int *groups, int *grpcnt)
412 {
413 struct group *grp;
414 int i, status;
415 uint32_t maxgroups, gg_count;
416 gid_t *gg_list;
417
418 status = 0;
419 maxgroups = (uint32_t)*grpcnt;
420 *grpcnt = 0;
421
422 gg_list = NULL;
423 gg_count = 0;
424
425 /*
426 * When installing primary group, duplicate it;
427 * the first element of groups is the effective gid
428 * and will be overwritten when a setgid file is executed.
429 */
430 _add_group(basegid, &gg_list, &gg_count, 0);
431 _add_group(basegid, &gg_list, &gg_count, 1);
432
433 if (gg_list == NULL)
434 {
435 errno = ENOMEM;
436 return 0;
437 }
438
439 /*
440 * Scan the group file to find additional groups.
441 */
442 setgrent();
443
444 while ((grp = getgrent()))
445 {
446 if (grp->gr_gid == (gid_t)basegid) continue;
447 for (i = 0; grp->gr_mem[i]; i++)
448 {
449 if (!strcmp(grp->gr_mem[i], uname))
450 {
451 _add_group(grp->gr_gid, &gg_list, &gg_count, 0);
452 break;
453 }
454 }
455 }
456
457 endgrent();
458
459 if (gg_list == NULL)
460 {
461 errno = ENOMEM;
462 return 0;
463 }
464
465 /* return -1 if the user-supplied list is too short */
466 status = 0;
467 if (gg_count > maxgroups) status = -1;
468
469 /* copy at most maxgroups gids from gg_list to groups */
470 for (i = 0; (i < maxgroups) && (i < gg_count); i++) groups[i] = gg_list[i];
471
472 *grpcnt = gg_count;
473 free(gg_list);
474
475 return status;
476 }
477
478 /*
479 * Guess at the size of a password buffer for getpwnam_r
480 * pw_name can be MAXLOGNAME + 1 256 - sys/param.h
481 * pw_passwd can be _PASSWORD_LEN + 1 129 - pwd.h
482 * pw_dir can be MAXPATHLEN + 1 1025 - sys/syslimits.h
483 * pw_shell can be MAXPATHLEN + 1025 - sys/syslimits.h
484 * We allow pw_class and pw_gecos to take a maximum of 4098 bytes (there's no limit on these).
485 * This adds to 6533 bytes (until one of the constants changes)
486 */
487 #define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098)
488
489 /*
490 * This is the "old" client side routine from memberd.
491 * It now talks to DirectoryService, but it retains the old style where
492 * the caller provides an array for the output gids. It fetches the
493 * user's gids from DS, then copies as many as possible into the
494 * caller-supplied array.
495 */
496 static int
497 mbr_getgrouplist(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
498 {
499 struct passwd p, *res;
500 char buf[MAXPWBUF];
501 kern_return_t kstatus;
502 uint32_t i, maxgroups, count, gg_count;
503 int pwstatus;
504 GIDArray gids;
505 gid_t *gidptr, *gg_list;
506 size_t gidptrsz;
507 int status, do_dealloc;
508 audit_token_t token;
509
510 if (_ds_port == MACH_PORT_NULL) return 0;
511 if (name == NULL) return 0;
512 if (groups == NULL) return 0;
513 if (grpcnt == NULL) return 0;
514
515 maxgroups = (uint32_t)(*grpcnt);
516 do_dealloc = 0;
517 *grpcnt = 0;
518 gidptr = NULL;
519 gidptrsz = 0;
520 gg_list = NULL;
521 gg_count = 0;
522
523 _add_group(basegid, &gg_list, &gg_count, 0);
524 if (dupbase != 0) _add_group(basegid, &gg_list, &gg_count, 1);
525
526 if (gg_list == NULL)
527 {
528 errno = ENOMEM;
529 return 0;
530 }
531
532 memset(&p, 0, sizeof(struct passwd));
533 memset(buf, 0, sizeof(buf));
534 res = NULL;
535
536 pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
537 if (pwstatus != 0) return 0;
538 if (res == NULL) return 0;
539
540 count = 0;
541 memset(&token, 0, sizeof(audit_token_t));
542
543 kstatus = 0;
544 if (maxgroups > 16)
545 {
546 uint32_t gidptrCnt = 0;
547 kstatus = memberdDSmig_GetAllGroups(_ds_port, p.pw_uid, &count, &gidptr, &gidptrCnt, &token);
548 gidptrsz = gidptrCnt * sizeof(gid_t);
549 do_dealloc = 1;
550 }
551 else
552 {
553 kstatus = memberdDSmig_GetGroups(_ds_port, p.pw_uid, &count, gids, &token);
554 gidptr = (gid_t *)gids;
555 }
556
557 if (kstatus != KERN_SUCCESS) return 0;
558 if (audit_token_uid(token) != 0)
559 {
560 if (gg_list != NULL) free(gg_list);
561 return 0;
562 }
563
564 for (i = 0; i < count; i++) _add_group(gidptr[i], &gg_list, &gg_count, 0);
565
566 if ((do_dealloc == 1) && (gidptr != NULL)) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrsz);
567
568 if (gg_list == NULL)
569 {
570 errno = ENOMEM;
571 return 0;
572 }
573
574 /* return -1 if the user-supplied list is too short */
575 status = 0;
576 if (gg_count > maxgroups) status = -1;
577
578 /* copy at most maxgroups gids from gg_list to groups */
579 for (i = 0; (i < maxgroups) && (i < gg_count); i++) groups[i] = gg_list[i];
580
581 *grpcnt = gg_count;
582 free(gg_list);
583
584 return status;
585 }
586
587 /*
588 * This is the "modern" routine for fetching the group list for a user.
589 * The grplist output parameter is allocated and filled with the gids
590 * of the specified user's groups. Returns the number of gids in the
591 * list or -1 on failure. Caller must free() the returns grplist.
592 */
593 static int32_t
594 ds_getgrouplist(const char *name, gid_t basegid, gid_t **grplist, int dupbase)
595 {
596 struct passwd p, *res;
597 char buf[MAXPWBUF];
598 kern_return_t kstatus;
599 uint32_t i, count, gidptrCnt, out_count;
600 int pwstatus;
601 gid_t *gidptr, *out_list;
602 size_t gidptrsz;
603 audit_token_t token;
604
605 if (_ds_port == MACH_PORT_NULL) return -1;
606 if (name == NULL) return -1;
607 if (grplist == NULL) return -1;
608
609 gidptr = NULL;
610 gidptrCnt = 0;
611 gidptrsz = 0;
612 out_list = NULL;
613 out_count = 0;
614
615 _add_group(basegid, &out_list, &out_count, 0);
616 if (dupbase != 0) _add_group(basegid, &out_list, &out_count, 1);
617
618 if (out_list == NULL) return -1;
619
620 memset(&p, 0, sizeof(struct passwd));
621 memset(buf, 0, sizeof(buf));
622 res = NULL;
623
624 pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
625 if (pwstatus != 0) return -1;
626 if (res == NULL) return -1;
627
628 count = 0;
629 memset(&token, 0, sizeof(audit_token_t));
630
631 kstatus = memberdDSmig_GetAllGroups(_ds_port, p.pw_uid, &count, &gidptr, &gidptrCnt, &token);
632 if (kstatus != KERN_SUCCESS)
633 {
634 if (out_list != NULL) free(out_list);
635 return -1;
636 }
637 gidptrsz = gidptrCnt * sizeof(gid_t);
638
639 if (audit_token_uid(token) != 0)
640 {
641 if (out_list != NULL) free(out_list);
642 if (gidptr != NULL) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrsz);
643 return -1;
644 }
645
646 for (i = 0; i < count; i++) _add_group(gidptr[i], &out_list, &out_count, 0);
647
648 if (gidptr != NULL) vm_deallocate(mach_task_self(), (vm_address_t)gidptr, gidptrsz);
649
650 *grplist = out_list;
651 return out_count;
652 }
653
654 static int
655 getgrouplist_internal(const char *name, int basegid, int *groups, int *grpcnt, int dupbase)
656 {
657 int status, in_grpcnt;
658
659 /*
660 * The man page says that the grpcnt parameter will be set to the actual number
661 * of groups that were found. Unfortunately, older impementations of this API
662 * have always set grpcnt to the number of groups that are being returned.
663 * To prevent regressions in callers of this API, we respect the old and
664 * incorrect implementation.
665 */
666
667 in_grpcnt = *grpcnt;
668 status = 0;
669
670 if (_ds_running()) status = mbr_getgrouplist(name, basegid, groups, grpcnt, dupbase);
671 else status = _old_getgrouplist(name, basegid, groups, grpcnt);
672
673 if ((status < 0) && (*grpcnt > in_grpcnt)) *grpcnt = in_grpcnt;
674 return status;
675 }
676
677 static int32_t
678 getgrouplist_internal_2(const char *name, gid_t basegid, gid_t **gid_list, int dupbase)
679 {
680 int status;
681 uint32_t gid_count;
682
683 if (name == NULL) return -1;
684 if (gid_list == NULL) return -1;
685
686 *gid_list = NULL;
687
688 if (_ds_running()) return ds_getgrouplist(name, basegid, gid_list, dupbase);
689
690 gid_count = NGROUPS + 1;
691 *gid_list = (gid_t *)calloc(gid_count, sizeof(gid_t));
692 if (*gid_list == NULL) return -1;
693
694 status = _old_getgrouplist(name, basegid, (int *)gid_list, (int *)&gid_count);
695 if (status < 0) return -1;
696 return gid_count;
697 }
698
699 int
700 getgrouplist(const char *uname, int agroup, int *groups, int *grpcnt)
701 {
702 return getgrouplist_internal(uname, agroup, groups, grpcnt, 0);
703 }
704
705 int32_t
706 getgrouplist_2(const char *uname, gid_t agroup, gid_t **groups)
707 {
708 return getgrouplist_internal_2(uname, agroup, groups, 0);
709 }
710
711 static void
712 ds_endgrent(void)
713 {
714 LI_data_free_kvarray(LI_data_find_key(ENTRY_KEY));
715 }
716
717 static void
718 ds_setgrent(void)
719 {
720 ds_endgrent();
721 }
722
723 static struct group *
724 ds_getgrent()
725 {
726 static int proc = -1;
727
728 return (struct group *)LI_getent("getgrent", &proc, extract_group, ENTRY_KEY, ENTRY_SIZE);
729 }
730
731 static struct group *
732 getgr_internal(const char *name, gid_t gid, int source)
733 {
734 struct group *res = NULL;
735 int add_to_cache;
736
737 add_to_cache = 0;
738 res = NULL;
739
740 switch (source)
741 {
742 case GR_GET_NAME:
743 res = cache_getgrnam(name);
744 break;
745 case GR_GET_GID:
746 res = cache_getgrgid(gid);
747 break;
748 default: res = NULL;
749 }
750
751 if (res != NULL)
752 {
753 }
754 else if (_ds_running())
755 {
756 switch (source)
757 {
758 case GR_GET_NAME:
759 res = ds_getgrnam(name);
760 break;
761 case GR_GET_GID:
762 res = ds_getgrgid(gid);
763 break;
764 case GR_GET_ENT:
765 res = ds_getgrent();
766 break;
767 default: res = NULL;
768 }
769
770 if (res != NULL) add_to_cache = 1;
771 }
772 else
773 {
774 pthread_mutex_lock(&_group_lock);
775
776 switch (source)
777 {
778 case GR_GET_NAME:
779 res = copy_group(_old_getgrnam(name));
780 break;
781 case GR_GET_GID:
782 res = copy_group(_old_getgrgid(gid));
783 break;
784 case GR_GET_ENT:
785 res = copy_group(_old_getgrent());
786 break;
787 default: res = NULL;
788 }
789
790 pthread_mutex_unlock(&_group_lock);
791 }
792
793 if (add_to_cache == 1) cache_group(res);
794
795 return res;
796 }
797
798 static struct group *
799 getgr(const char *name, gid_t gid, int source)
800 {
801 struct group *res = NULL;
802 struct li_thread_info *tdata;
803
804 tdata = LI_data_create_key(ENTRY_KEY, ENTRY_SIZE);
805 if (tdata == NULL) return NULL;
806
807 res = getgr_internal(name, gid, source);
808
809 LI_data_recycle(tdata, res, ENTRY_SIZE);
810 return (struct group *)tdata->li_entry;
811 }
812
813 static int
814 getgr_r(const char *name, gid_t gid, int source, struct group *grp, char *buffer, size_t bufsize, struct group **result)
815 {
816 struct group *res = NULL;
817 int status;
818
819 *result = NULL;
820
821 res = getgr_internal(name, gid, source);
822 if (res == NULL) return 0;
823
824 status = copy_group_r(res, grp, buffer, bufsize);
825
826 LI_ils_free(res, ENTRY_SIZE);
827
828 if (status != 0) return ERANGE;
829
830 *result = grp;
831 return 0;
832 }
833
834 int
835 initgroups(const char *name, int basegid)
836 {
837 int status, pwstatus, ngroups, groups[NGROUPS];
838 struct passwd p, *res;
839 char buf[MAXPWBUF];
840
841 /* get the UID for this user */
842 memset(&p, 0, sizeof(struct passwd));
843 memset(buf, 0, sizeof(buf));
844 res = NULL;
845
846 pwstatus = getpwnam_r(name, &p, buf, MAXPWBUF, &res);
847 if (pwstatus != 0) return -1;
848 if (res == NULL) return -1;
849
850 ngroups = NGROUPS;
851
852 status = getgrouplist_internal(name, basegid, groups, &ngroups, 0);
853 if (status < 0) return status;
854
855 status = syscall(SYS_initgroups, ngroups, groups, p.pw_uid);
856 if (status < 0) return -1;
857
858 return 0;
859 }
860
861 struct group *
862 getgrnam(const char *name)
863 {
864 return getgr(name, -2, GR_GET_NAME);
865 }
866
867 struct group *
868 getgrgid(gid_t gid)
869 {
870 return getgr(NULL, gid, GR_GET_GID);
871 }
872
873 struct group *
874 getgrent(void)
875 {
876 return getgr(NULL, -2, GR_GET_ENT);
877 }
878
879 int
880 setgrent(void)
881 {
882 if (_ds_running()) ds_setgrent();
883 else _old_setgrent();
884 return 1;
885 }
886
887 void
888 endgrent(void)
889 {
890 if (_ds_running()) ds_endgrent();
891 else _old_endgrent();
892 }
893
894 int
895 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result)
896 {
897 return getgr_r(name, -2, GR_GET_NAME, grp, buffer, bufsize, result);
898 }
899
900 int
901 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result)
902 {
903 return getgr_r(NULL, gid, GR_GET_GID, grp, buffer, bufsize, result);
904 }