2 * Copyright (c) 1999-2002 Apple Computer, 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>
34 #include <rpc/types.h>
38 #include <netinet/in.h>
39 #include <sys/param.h>
43 #include <servers/bootstrap.h>
45 #include "_lu_types.h"
48 #include "lu_overrides.h"
50 #define GROUP_CACHE_SIZE 10
51 #define DEFAULT_GROUP_CACHE_TTL 10
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
;
59 static pthread_mutex_t _group_lock
= PTHREAD_MUTEX_INITIALIZER
;
62 * Support for memberd calls
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
);
74 free_group_data(struct group
*g
)
78 if (g
== NULL
) return;
80 if (g
->gr_name
!= NULL
) free(g
->gr_name
);
81 if (g
->gr_passwd
!= NULL
) free(g
->gr_passwd
);
86 while (*mem
!= NULL
) free(*mem
++);
92 free_group(struct group
*g
)
94 if (g
== NULL
) return;
100 free_lu_thread_info_group(void *x
)
102 struct lu_thread_info
*tdata
;
104 if (x
== NULL
) return;
106 tdata
= (struct lu_thread_info
*)x
;
108 if (tdata
->lu_entry
!= NULL
)
110 free_group((struct group
*)tdata
->lu_entry
);
111 tdata
->lu_entry
= NULL
;
114 _lu_data_free_vm_xdr(tdata
);
119 static struct group
*
120 extract_group(XDR
*xdr
)
122 int i
, j
, nkeys
, nvals
, status
;
126 if (xdr
== NULL
) return NULL
;
128 if (!xdr_int(xdr
, &nkeys
)) return NULL
;
130 g
= (struct group
*)calloc(1, sizeof(struct group
));
133 for (i
= 0; i
< nkeys
; i
++)
139 status
= _lu_xdr_attribute(xdr
, &key
, &vals
, &nvals
);
154 if ((g
->gr_name
== NULL
) && (!strcmp("name", key
)))
156 g
->gr_name
= vals
[0];
159 else if ((g
->gr_passwd
== NULL
) && (!strcmp("passwd", key
)))
161 g
->gr_passwd
= vals
[0];
164 else if ((g
->gr_gid
== (gid_t
)-2) && (!strcmp("gid", key
)))
166 g
->gr_gid
= atoi(vals
[0]);
167 if ((g
->gr_gid
== 0) && (strcmp(vals
[0], "0"))) g
->gr_gid
= -2;
169 else if ((g
->gr_mem
== NULL
) && (!strcmp("users", key
)))
179 for (; j
< nvals
; j
++) free(vals
[j
]);
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 *));
191 static struct group
*
192 copy_group(struct group
*in
)
197 if (in
== NULL
) return NULL
;
199 g
= (struct group
*)calloc(1, sizeof(struct group
));
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
;
206 if (in
->gr_mem
!= NULL
)
208 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++);
211 g
->gr_mem
= (char **)calloc(len
+ 1, sizeof(char *));
212 for (i
= 0; i
< len
; i
++)
214 g
->gr_mem
[i
] = strdup(in
->gr_mem
[i
]);
221 copy_group_r(struct group
*in
, struct group
*out
, char *buffer
, int buflen
)
227 if (in
== NULL
) return -1;
228 if (out
== NULL
) return -1;
230 if (buffer
== NULL
) buflen
= 0;
232 /* Calculate size of input */
234 if (in
->gr_name
!= NULL
) hsize
+= (strlen(in
->gr_name
) + 1);
235 if (in
->gr_passwd
!= NULL
) hsize
+= (strlen(in
->gr_passwd
) + 1);
237 /* NULL pointer at end of list */
238 hsize
+= sizeof(char *);
241 if (in
->gr_mem
!= NULL
)
243 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++)
245 hsize
+= sizeof(char *);
246 hsize
+= (strlen(in
->gr_mem
[len
]) + 1);
250 /* Check buffer space */
251 if (hsize
> buflen
) return -1;
253 /* Copy result into caller's struct group, using buffer for memory */
257 if (in
->gr_name
!= NULL
)
260 hsize
= strlen(in
->gr_name
) + 1;
261 memmove(bp
, in
->gr_name
, hsize
);
265 out
->gr_passwd
= NULL
;
266 if (in
->gr_passwd
!= NULL
)
269 hsize
= strlen(in
->gr_passwd
) + 1;
270 memmove(bp
, in
->gr_passwd
, hsize
);
274 out
->gr_gid
= in
->gr_gid
;
277 ap
= bp
+ ((len
+ 1) * sizeof(char *));
279 if (in
->gr_mem
!= NULL
)
281 out
->gr_mem
= (char **)bp
;
282 for (i
= 0; i
< len
; i
++)
284 addr
= (unsigned long)ap
;
285 memmove(bp
, &addr
, sizeof(unsigned long));
286 bp
+= sizeof(unsigned long);
288 hsize
= strlen(in
->gr_mem
[i
]) + 1;
289 memmove(ap
, in
->gr_mem
[i
], hsize
);
294 memset(bp
, 0, sizeof(unsigned long));
301 recycle_group(struct lu_thread_info
*tdata
, struct group
*in
)
305 if (tdata
== NULL
) return;
306 g
= (struct group
*)tdata
->lu_entry
;
311 tdata
->lu_entry
= NULL
;
314 if (tdata
->lu_entry
== NULL
)
316 tdata
->lu_entry
= in
;
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
;
330 __private_extern__
unsigned int
331 get_group_cache_ttl()
333 return _group_cache_ttl
;
336 __private_extern__
void
337 set_group_cache_ttl(unsigned int ttl
)
341 pthread_mutex_lock(&_group_cache_lock
);
343 _group_cache_ttl
= ttl
;
347 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
349 if (_group_cache
[i
] == NULL
) continue;
351 free_group((struct group
*)_group_cache
[i
]);
352 _group_cache
[i
] = NULL
;
353 _group_cache_best_before
[i
] = 0;
357 pthread_mutex_unlock(&_group_cache_lock
);
361 cache_group(struct group
*gr
)
364 struct group
*grcache
;
366 if (_group_cache_ttl
== 0) return;
367 if (gr
== NULL
) return;
369 pthread_mutex_lock(&_group_cache_lock
);
371 grcache
= copy_group(gr
);
373 gettimeofday(&now
, NULL
);
375 if (_group_cache
[_group_cache_index
] != NULL
)
376 free_group((struct group
*)_group_cache
[_group_cache_index
]);
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
;
382 pthread_mutex_unlock(&_group_cache_lock
);
385 static struct group
*
386 cache_getgrnam(const char *name
)
389 struct group
*gr
, *res
;
392 if (_group_cache_ttl
== 0) return NULL
;
393 if (name
== NULL
) return NULL
;
395 pthread_mutex_lock(&_group_cache_lock
);
397 gettimeofday(&now
, NULL
);
399 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
401 if (_group_cache_best_before
[i
] == 0) continue;
402 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
404 gr
= (struct group
*)_group_cache
[i
];
406 if (gr
->gr_name
== NULL
) continue;
408 if (!strcmp(name
, gr
->gr_name
))
410 res
= copy_group(gr
);
411 pthread_mutex_unlock(&_group_cache_lock
);
416 pthread_mutex_unlock(&_group_cache_lock
);
420 static struct group
*
421 cache_getgrgid(int gid
)
424 struct group
*gr
, *res
;
427 if (_group_cache_ttl
== 0) return NULL
;
429 pthread_mutex_lock(&_group_cache_lock
);
431 gettimeofday(&now
, NULL
);
433 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
435 if (_group_cache_best_before
[i
] == 0) continue;
436 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
438 gr
= (struct group
*)_group_cache
[i
];
440 if ((gid_t
)gid
== gr
->gr_gid
)
442 res
= copy_group(gr
);
443 pthread_mutex_unlock(&_group_cache_lock
);
448 pthread_mutex_unlock(&_group_cache_lock
);
452 static struct group
*
456 unsigned int datalen
;
458 static int proc
= -1;
464 if (_lookup_link(_lu_port
, "getgrgid", &proc
) != KERN_SUCCESS
)
474 if (_lookup_all(_lu_port
, proc
, (unit
*)&gid
, 1, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
479 datalen
*= BYTES_PER_XDR_UNIT
;
480 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
482 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
485 if (!xdr_int(&inxdr
, &count
))
488 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
495 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
499 g
= extract_group(&inxdr
);
501 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
506 static struct group
*
507 lu_getgrnam(const char *name
)
510 unsigned int datalen
;
511 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
514 static int proc
= -1;
520 if (_lookup_link(_lu_port
, "getgrnam", &proc
) != KERN_SUCCESS
)
526 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
528 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
530 xdr_destroy(&outxdr
);
537 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
, xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
542 xdr_destroy(&outxdr
);
544 datalen
*= BYTES_PER_XDR_UNIT
;
545 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
547 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
550 if (!xdr_int(&inxdr
, &count
))
553 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
560 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
564 g
= extract_group(&inxdr
);
566 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
572 * add a group to a list
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
581 _add_group(int gid
, int *list
, int *listcount
, int max
, int dupok
, int laststatus
)
583 int i
, n
, addit
, status
;
585 if (laststatus
!= 0) return laststatus
;
593 for (i
= 0; (i
< n
) && (addit
== 1); i
++)
595 if (list
[i
] == gid
) addit
= 0;
599 if (addit
== 0) return 0;
600 if (n
>= max
) return -1;
608 _old_getgrouplist(const char *uname
, int basegid
, int *groups
, int *grpcnt
)
611 int i
, status
, maxgroups
;
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.
622 status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 0, status
);
623 status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 1, status
);
626 * Scan the group file to find additional groups.
630 while ((grp
= getgrent()))
632 if (grp
->gr_gid
== (gid_t
)basegid
) continue;
633 for (i
= 0; grp
->gr_mem
[i
]; i
++)
635 if (!strcmp(grp
->gr_mem
[i
], uname
))
637 status
= _add_group(grp
->gr_gid
, groups
, grpcnt
, maxgroups
, 0, status
);
650 kern_return_t status
;
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;
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)
667 #define MAXPWBUF (MAXLOGNAME + 1 + _PASSWORD_LEN + 1 + MAXPATHLEN + 1 + MAXPATHLEN + 1 + 4098)
669 mbr_getgrouplist(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
671 struct passwd p
, *res
;
673 kern_return_t kstatus
;
677 int status
, maxgroups
;
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
;
689 status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 0, status
);
690 if (dupbase
!= 0) status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 1, status
);
692 if (status
!= 0) return status
;
694 memset(&p
, 0, sizeof(struct passwd
));
695 memset(buf
, 0, sizeof(buf
));
698 pwstatus
= getpwnam_r(name
, &p
, buf
, MAXPWBUF
, &res
);
699 if (pwstatus
!= 0) return status
;
700 if (res
== NULL
) return status
;
703 kstatus
= _mbr_GetGroups(mbr_port
, p
.pw_uid
, &count
, gids
);
704 if (kstatus
!= KERN_SUCCESS
) return status
;
706 for (i
= 0; (i
< count
) && (status
== 0); i
++)
708 status
= _add_group(gids
[i
], groups
, grpcnt
, maxgroups
, 0, status
);
715 lu_getgrouplist(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
717 unsigned int datalen
;
720 static int proc
= -1;
722 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
725 int status
, maxgroups
;
729 if (name
== NULL
) return status
;
730 if (groups
== NULL
) return status
;
731 if (grpcnt
== NULL
) return status
;
736 status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 0, status
);
737 if (dupbase
!= 0) status
= _add_group(basegid
, groups
, grpcnt
, maxgroups
, 1, status
);
739 if (status
!= 0) return status
;
743 if (_lookup_link(_lu_port
, "initgroups", &proc
) != KERN_SUCCESS
) return status
;
746 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
747 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
749 xdr_destroy(&outxdr
);
756 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
, xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
758 xdr_destroy(&outxdr
);
762 xdr_destroy(&outxdr
);
764 datalen
*= BYTES_PER_XDR_UNIT
;
765 if ((lookup_buf
== NULL
) || (datalen
== 0)) return 0;
767 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
769 if (!xdr_int(&inxdr
, &count
))
772 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
776 for (i
= 0; (i
< count
) && (status
== 0); i
++)
778 if (!xdr_int(&inxdr
, &gid
)) break;
779 status
= _add_group(gid
, groups
, grpcnt
, maxgroups
, 0, status
);
783 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
789 getgrouplist_internal(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
793 return mbr_getgrouplist(name
, basegid
, groups
, grpcnt
, dupbase
);
798 return lu_getgrouplist(name
, basegid
, groups
, grpcnt
, dupbase
);
801 return _old_getgrouplist(name
, basegid
, groups
, grpcnt
);
805 getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
807 return getgrouplist_internal(uname
, agroup
, groups
, grpcnt
, 1);
813 struct lu_thread_info
*tdata
;
815 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
816 _lu_data_free_vm_xdr(tdata
);
826 static struct group
*
830 static int proc
= -1;
831 struct lu_thread_info
*tdata
;
833 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
836 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
837 _lu_data_set_key(_lu_data_key_group
, tdata
);
840 if (tdata
->lu_vm
== NULL
)
844 if (_lookup_link(_lu_port
, "getgrent", &proc
) != KERN_SUCCESS
)
851 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
857 /* mig stubs measure size in words (4 bytes) */
858 tdata
->lu_vm_length
*= 4;
860 if (tdata
->lu_xdr
!= NULL
)
862 xdr_destroy(tdata
->lu_xdr
);
865 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
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
))
875 if (tdata
->lu_vm_cursor
== 0)
881 g
= extract_group(tdata
->lu_xdr
);
888 tdata
->lu_vm_cursor
--;
893 static struct group
*
894 getgr_internal(const char *name
, gid_t gid
, int source
)
896 struct group
*res
= NULL
;
905 res
= cache_getgrnam(name
);
908 res
= cache_getgrgid(gid
);
917 else if (_lu_running())
922 res
= lu_getgrnam(name
);
925 res
= lu_getgrgid(gid
);
935 pthread_mutex_lock(&_group_lock
);
939 res
= copy_group(_old_getgrnam(name
));
942 res
= copy_group(_old_getgrgid(gid
));
945 res
= copy_group(_old_getgrent());
949 pthread_mutex_unlock(&_group_lock
);
952 if (from_cache
== 0) cache_group(res
);
957 static struct group
*
958 getgr(const char *name
, gid_t gid
, int source
)
960 struct group
*res
= NULL
;
961 struct lu_thread_info
*tdata
;
963 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
966 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
967 _lu_data_set_key(_lu_data_key_group
, tdata
);
970 res
= getgr_internal(name
, gid
, source
);
972 recycle_group(tdata
, res
);
973 return (struct group
*)tdata
->lu_entry
;
977 getgr_r(const char *name
, gid_t gid
, int source
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
979 struct group
*res
= NULL
;
985 res
= getgr_internal(name
, gid
, source
);
986 if (res
== NULL
) return -1;
988 status
= copy_group_r(res
, grp
, buffer
, bufsize
);
1002 initgroups(const char *name
, int basegid
)
1004 int status
, ngroups
, groups
[NGROUPS
];
1008 status
= getgrouplist_internal(name
, basegid
, groups
, &ngroups
, 0);
1009 if (status
< 0) return status
;
1011 return setgroups(ngroups
, groups
);
1015 getgrnam(const char *name
)
1017 return getgr(name
, -2, GR_GET_NAME
);
1023 return getgr(NULL
, gid
, GR_GET_GID
);
1029 return getgr(NULL
, -2, GR_GET_ENT
);
1035 if (_lu_running()) lu_setgrent();
1036 else _old_setgrent();
1043 if (_lu_running()) lu_endgrent();
1044 else _old_endgrent();
1048 getgrnam_r(const char *name
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
1050 return getgr_r(name
, -2, GR_GET_NAME
, grp
, buffer
, bufsize
, result
);
1054 getgrgid_r(gid_t gid
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
1056 return getgr_r(NULL
, gid
, GR_GET_GID
, grp
, buffer
, bufsize
, result
);