2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (C) 1989 by NeXT, Inc.
30 #include <mach/mach.h>
35 #include <netinet/in.h>
36 #include <sys/param.h>
40 #include <servers/bootstrap.h>
41 #include <sys/syscall.h>
43 #include "lu_overrides.h"
45 #define ENTRY_SIZE sizeof(struct group)
46 #define ENTRY_KEY _li_data_key_group
47 #define GROUP_CACHE_SIZE 10
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;
54 static pthread_mutex_t _group_lock
= PTHREAD_MUTEX_INITIALIZER
;
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.
71 struct group
*getgrent(void);
75 * Support for memberd calls
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
);
88 copy_group(struct group
*in
)
90 if (in
== NULL
) return NULL
;
92 return (struct group
*)LI_ils_create("ss4*", in
->gr_name
, in
->gr_passwd
, in
->gr_gid
, in
->gr_mem
);
96 * Extract the next group entry from a kvarray.
99 extract_group(kvarray_t
*in
)
102 uint32_t d
, k
, kcount
;
105 if (in
== NULL
) return NULL
;
110 if (d
>= in
->count
) return NULL
;
113 memset(&tmp
, 0, ENTRY_SIZE
);
117 kcount
= in
->dict
[d
].kcount
;
119 for (k
= 0; k
< kcount
; k
++)
121 if (!strcmp(in
->dict
[d
].key
[k
], "gr_name"))
123 if (tmp
.gr_name
!= NULL
) continue;
124 if (in
->dict
[d
].vcount
[k
] == 0) continue;
126 tmp
.gr_name
= (char *)in
->dict
[d
].val
[k
][0];
128 else if (!strcmp(in
->dict
[d
].key
[k
], "gr_passwd"))
130 if (tmp
.gr_passwd
!= NULL
) continue;
131 if (in
->dict
[d
].vcount
[k
] == 0) continue;
133 tmp
.gr_passwd
= (char *)in
->dict
[d
].val
[k
][0];
135 else if (!strcmp(in
->dict
[d
].key
[k
], "gr_gid"))
137 if (in
->dict
[d
].vcount
[k
] == 0) continue;
138 tmp
.gr_gid
= atoi(in
->dict
[d
].val
[k
][0]);
140 else if (!strcmp(in
->dict
[d
].key
[k
], "gr_mem"))
142 if (tmp
.gr_mem
!= NULL
) continue;
143 if (in
->dict
[d
].vcount
[k
] == 0) continue;
145 tmp
.gr_mem
= (char **)in
->dict
[d
].val
[k
];
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
;
153 return copy_group(&tmp
);
157 copy_group_r(struct group
*in
, struct group
*out
, char *buffer
, int buflen
)
163 if (in
== NULL
) return -1;
164 if (out
== NULL
) return -1;
166 if (buffer
== NULL
) buflen
= 0;
168 /* Calculate size of input */
170 if (in
->gr_name
!= NULL
) hsize
+= (strlen(in
->gr_name
) + 1);
171 if (in
->gr_passwd
!= NULL
) hsize
+= (strlen(in
->gr_passwd
) + 1);
173 /* NULL pointer at end of list */
174 hsize
+= sizeof(char *);
177 if (in
->gr_mem
!= NULL
)
179 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++)
181 hsize
+= sizeof(char *);
182 hsize
+= (strlen(in
->gr_mem
[len
]) + 1);
186 /* Check buffer space */
187 if (hsize
> buflen
) return -1;
189 /* Copy result into caller's struct group, using buffer for memory */
193 if (in
->gr_name
!= NULL
)
196 hsize
= strlen(in
->gr_name
) + 1;
197 memmove(bp
, in
->gr_name
, hsize
);
201 out
->gr_passwd
= NULL
;
202 if (in
->gr_passwd
!= NULL
)
205 hsize
= strlen(in
->gr_passwd
) + 1;
206 memmove(bp
, in
->gr_passwd
, hsize
);
210 out
->gr_gid
= in
->gr_gid
;
213 ap
= bp
+ ((len
+ 1) * sizeof(char *));
215 if (in
->gr_mem
!= NULL
)
217 out
->gr_mem
= (char **)bp
;
218 for (i
= 0; i
< len
; i
++)
220 addr
= (unsigned long)ap
;
221 memmove(bp
, &addr
, sizeof(unsigned long));
222 bp
+= sizeof(unsigned long);
224 hsize
= strlen(in
->gr_mem
[i
]) + 1;
225 memmove(ap
, in
->gr_mem
[i
], hsize
);
230 memset(bp
, 0, sizeof(unsigned long));
237 cache_group(struct group
*gr
)
239 struct group
*grcache
;
241 if (gr
== NULL
) return;
243 pthread_mutex_lock(&_group_cache_lock
);
245 grcache
= copy_group(gr
);
247 if (_group_cache
[_group_cache_index
] != NULL
) LI_ils_free(_group_cache
[_group_cache_index
], ENTRY_SIZE
);
249 _group_cache
[_group_cache_index
] = grcache
;
250 _group_cache_index
= (_group_cache_index
+ 1) % GROUP_CACHE_SIZE
;
252 _group_cache_init
= 1;
254 pthread_mutex_unlock(&_group_cache_lock
);
262 /* don't consult cache if it has not been initialized */
263 if (_group_cache_init
== 0) return 1;
265 status
= LI_L1_cache_check(ENTRY_KEY
);
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;
270 /* return 0 if cache is OK */
271 if (status
== LI_L1_CACHE_OK
) return 0;
274 pthread_mutex_lock(&_group_cache_lock
);
276 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
278 LI_ils_free(_group_cache
[i
], ENTRY_SIZE
);
279 _group_cache
[i
] = NULL
;
282 _group_cache_index
= 0;
284 pthread_mutex_unlock(&_group_cache_lock
);
286 /* don't consult cache - it's now empty */
291 static struct group
*
292 cache_getgrnam(const char *name
)
295 struct group
*gr
, *res
;
297 if (name
== NULL
) return NULL
;
298 if (group_cache_check() != 0) return NULL
;
300 pthread_mutex_lock(&_group_cache_lock
);
302 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
304 gr
= (struct group
*)_group_cache
[i
];
305 if (gr
== NULL
) continue;
307 if (gr
->gr_name
== NULL
) continue;
309 if (!strcmp(name
, gr
->gr_name
))
311 res
= copy_group(gr
);
312 pthread_mutex_unlock(&_group_cache_lock
);
317 pthread_mutex_unlock(&_group_cache_lock
);
321 static struct group
*
322 cache_getgrgid(int gid
)
325 struct group
*gr
, *res
;
327 if (group_cache_check() != 0) return NULL
;
329 pthread_mutex_lock(&_group_cache_lock
);
331 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
333 gr
= (struct group
*)_group_cache
[i
];
334 if (gr
== NULL
) continue;
336 if ((gid_t
)gid
== gr
->gr_gid
)
338 res
= copy_group(gr
);
339 pthread_mutex_unlock(&_group_cache_lock
);
344 pthread_mutex_unlock(&_group_cache_lock
);
348 static struct group
*
351 static int proc
= -1;
354 snprintf(val
, sizeof(val
), "%d", gid
);
355 return (struct group
*)LI_getone("getgrgid", &proc
, extract_group
, "gid", val
);
358 static struct group
*
359 ds_getgrnam(const char *name
)
361 static int proc
= -1;
363 return (struct group
*)LI_getone("getgrnam", &proc
, extract_group
, "name", name
);
367 * add a group to a list
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
376 _add_group(gid_t g
, gid_t
**list
, uint32_t *count
, int dupok
)
378 uint32_t i
, n
, addit
;
382 if (list
== NULL
) return;
383 if (*list
== NULL
) *count
= 0;
389 for (i
= 0; (i
< n
) && (addit
== 1); i
++)
391 if ((*list
)[i
] == g
) addit
= 0;
395 if (addit
== 0) return;
397 if (*list
== NULL
) *list
= (gid_t
*)calloc(1, sizeof(gid_t
));
398 else *list
= (gid_t
*)realloc(*list
, (n
+ 1) * sizeof(gid_t
));
411 _old_getgrouplist(const char *uname
, int basegid
, int *groups
, int *grpcnt
)
415 uint32_t maxgroups
, gg_count
;
419 maxgroups
= (uint32_t)*grpcnt
;
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.
430 _add_group(basegid
, &gg_list
, &gg_count
, 0);
431 _add_group(basegid
, &gg_list
, &gg_count
, 1);
440 * Scan the group file to find additional groups.
444 while ((grp
= getgrent()))
446 if (grp
->gr_gid
== (gid_t
)basegid
) continue;
447 for (i
= 0; grp
->gr_mem
[i
]; i
++)
449 if (!strcmp(grp
->gr_mem
[i
], uname
))
451 _add_group(grp
->gr_gid
, &gg_list
, &gg_count
, 0);
465 /* return -1 if the user-supplied list is too short */
467 if (gg_count
> maxgroups
) status
= -1;
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
];
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)
487 #define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098)
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.
497 mbr_getgrouplist(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
499 struct passwd p
, *res
;
501 kern_return_t kstatus
;
502 uint32_t i
, maxgroups
, count
, gidptrCnt
, gg_count
;
505 gid_t
*gidptr
, *gg_list
;
506 int status
, do_dealloc
;
509 if (_ds_port
== MACH_PORT_NULL
) return 0;
510 if (name
== NULL
) return 0;
511 if (groups
== NULL
) return 0;
512 if (grpcnt
== NULL
) return 0;
514 maxgroups
= (uint32_t)(*grpcnt
);
522 _add_group(basegid
, &gg_list
, &gg_count
, 0);
523 if (dupbase
!= 0) _add_group(basegid
, &gg_list
, &gg_count
, 1);
531 memset(&p
, 0, sizeof(struct passwd
));
532 memset(buf
, 0, sizeof(buf
));
535 pwstatus
= getpwnam_r(name
, &p
, buf
, MAXPWBUF
, &res
);
536 if (pwstatus
!= 0) return 0;
537 if (res
== NULL
) return 0;
540 memset(&token
, 0, sizeof(audit_token_t
));
545 kstatus
= memberdDSmig_GetAllGroups(_ds_port
, p
.pw_uid
, &count
, &gidptr
, &gidptrCnt
, &token
);
550 kstatus
= memberdDSmig_GetGroups(_ds_port
, p
.pw_uid
, &count
, gids
, &token
);
551 gidptr
= (gid_t
*)gids
;
554 if (kstatus
!= KERN_SUCCESS
) return 0;
555 if (audit_token_uid(token
) != 0)
557 if (gg_list
!= NULL
) free(gg_list
);
561 for (i
= 0; i
< count
; i
++) _add_group(gidptr
[i
], &gg_list
, &gg_count
, 0);
563 if ((do_dealloc
== 1) && (gidptr
!= NULL
)) vm_deallocate(mach_task_self(), (vm_address_t
)gidptr
, gidptrCnt
);
571 /* return -1 if the user-supplied list is too short */
573 if (gg_count
> maxgroups
) status
= -1;
575 /* copy at most maxgroups gids from gg_list to groups */
576 for (i
= 0; (i
< maxgroups
) && (i
< gg_count
); i
++) groups
[i
] = gg_list
[i
];
585 * This is the "modern" routine for fetching the group list for a user.
586 * The grplist output parameter is allocated and filled with the gids
587 * of the specified user's groups. Returns the number of gids in the
588 * list or -1 on failure. Caller must free() the returns grplist.
591 ds_getgrouplist(const char *name
, gid_t basegid
, gid_t
**grplist
, int dupbase
)
593 struct passwd p
, *res
;
595 kern_return_t kstatus
;
596 uint32_t i
, count
, gidptrCnt
, out_count
;
598 gid_t
*gidptr
, *out_list
;
601 if (_ds_port
== MACH_PORT_NULL
) return -1;
602 if (name
== NULL
) return -1;
603 if (grplist
== NULL
) return -1;
610 _add_group(basegid
, &out_list
, &out_count
, 0);
611 if (dupbase
!= 0) _add_group(basegid
, &out_list
, &out_count
, 1);
613 if (out_list
== NULL
) return -1;
615 memset(&p
, 0, sizeof(struct passwd
));
616 memset(buf
, 0, sizeof(buf
));
619 pwstatus
= getpwnam_r(name
, &p
, buf
, MAXPWBUF
, &res
);
620 if (pwstatus
!= 0) return -1;
621 if (res
== NULL
) return -1;
624 memset(&token
, 0, sizeof(audit_token_t
));
626 kstatus
= memberdDSmig_GetAllGroups(_ds_port
, p
.pw_uid
, &count
, &gidptr
, &gidptrCnt
, &token
);
627 if (kstatus
!= KERN_SUCCESS
)
629 if (out_list
!= NULL
) free(out_list
);
633 if (audit_token_uid(token
) != 0)
635 if (out_list
!= NULL
) free(out_list
);
636 if (gidptr
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)gidptr
, gidptrCnt
);
640 for (i
= 0; i
< count
; i
++) _add_group(gidptr
[i
], &out_list
, &out_count
, 0);
642 if (gidptr
!= NULL
) vm_deallocate(mach_task_self(), (vm_address_t
)gidptr
, gidptrCnt
);
649 getgrouplist_internal(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
651 int status
, in_grpcnt
;
654 * The man page says that the grpcnt parameter will be set to the actual number
655 * of groups that were found. Unfortunately, older impementations of this API
656 * have always set grpcnt to the number of groups that are being returned.
657 * To prevent regressions in callers of this API, we respect the old and
658 * incorrect implementation.
664 if (_ds_running()) status
= mbr_getgrouplist(name
, basegid
, groups
, grpcnt
, dupbase
);
665 else status
= _old_getgrouplist(name
, basegid
, groups
, grpcnt
);
667 if ((status
< 0) && (*grpcnt
> in_grpcnt
)) *grpcnt
= in_grpcnt
;
672 getgrouplist_internal_2(const char *name
, gid_t basegid
, gid_t
**gid_list
, int dupbase
)
677 if (name
== NULL
) return -1;
678 if (gid_list
== NULL
) return -1;
682 if (_ds_running()) return ds_getgrouplist(name
, basegid
, gid_list
, dupbase
);
684 gid_count
= NGROUPS
+ 1;
685 *gid_list
= (gid_t
*)calloc(gid_count
, sizeof(gid_t
));
686 if (*gid_list
== NULL
) return -1;
688 status
= _old_getgrouplist(name
, basegid
, (int *)gid_list
, (int *)&gid_count
);
689 if (status
< 0) return -1;
694 getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
696 return getgrouplist_internal(uname
, agroup
, groups
, grpcnt
, 0);
700 getgrouplist_2(const char *uname
, gid_t agroup
, gid_t
**groups
)
702 return getgrouplist_internal_2(uname
, agroup
, groups
, 0);
708 LI_data_free_kvarray(LI_data_find_key(ENTRY_KEY
));
717 static struct group
*
720 static int proc
= -1;
722 return (struct group
*)LI_getent("getgrent", &proc
, extract_group
, ENTRY_KEY
, ENTRY_SIZE
);
725 static struct group
*
726 getgr_internal(const char *name
, gid_t gid
, int source
)
728 struct group
*res
= NULL
;
737 res
= cache_getgrnam(name
);
740 res
= cache_getgrgid(gid
);
748 else if (_ds_running())
753 res
= ds_getgrnam(name
);
756 res
= ds_getgrgid(gid
);
764 if (res
!= NULL
) add_to_cache
= 1;
768 pthread_mutex_lock(&_group_lock
);
773 res
= copy_group(_old_getgrnam(name
));
776 res
= copy_group(_old_getgrgid(gid
));
779 res
= copy_group(_old_getgrent());
784 pthread_mutex_unlock(&_group_lock
);
787 if (add_to_cache
== 1) cache_group(res
);
792 static struct group
*
793 getgr(const char *name
, gid_t gid
, int source
)
795 struct group
*res
= NULL
;
796 struct li_thread_info
*tdata
;
798 tdata
= LI_data_create_key(ENTRY_KEY
, ENTRY_SIZE
);
799 if (tdata
== NULL
) return NULL
;
801 res
= getgr_internal(name
, gid
, source
);
803 LI_data_recycle(tdata
, res
, ENTRY_SIZE
);
804 return (struct group
*)tdata
->li_entry
;
808 getgr_r(const char *name
, gid_t gid
, int source
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
810 struct group
*res
= NULL
;
815 res
= getgr_internal(name
, gid
, source
);
816 if (res
== NULL
) return 0;
818 status
= copy_group_r(res
, grp
, buffer
, bufsize
);
820 LI_ils_free(res
, ENTRY_SIZE
);
822 if (status
!= 0) return ERANGE
;
829 initgroups(const char *name
, int basegid
)
831 int status
, pwstatus
, ngroups
, groups
[NGROUPS
];
832 struct passwd p
, *res
;
835 /* get the UID for this user */
836 memset(&p
, 0, sizeof(struct passwd
));
837 memset(buf
, 0, sizeof(buf
));
840 pwstatus
= getpwnam_r(name
, &p
, buf
, MAXPWBUF
, &res
);
841 if (pwstatus
!= 0) return -1;
842 if (res
== NULL
) return -1;
846 status
= getgrouplist_internal(name
, basegid
, groups
, &ngroups
, 0);
847 if (status
< 0) return status
;
849 status
= syscall(SYS_initgroups
, ngroups
, groups
, p
.pw_uid
);
850 if (status
< 0) return -1;
856 getgrnam(const char *name
)
858 return getgr(name
, -2, GR_GET_NAME
);
864 return getgr(NULL
, gid
, GR_GET_GID
);
870 return getgr(NULL
, -2, GR_GET_ENT
);
876 if (_ds_running()) ds_setgrent();
877 else _old_setgrent();
884 if (_ds_running()) ds_endgrent();
885 else _old_endgrent();
889 getgrnam_r(const char *name
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
891 return getgr_r(name
, -2, GR_GET_NAME
, grp
, buffer
, bufsize
, result
);
895 getgrgid_r(gid_t gid
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
897 return getgr_r(NULL
, gid
, GR_GET_GID
, grp
, buffer
, bufsize
, result
);