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>
37 #include <netinet/in.h>
38 #include <sys/param.h>
42 #include "_lu_types.h"
45 #include "lu_overrides.h"
47 #define GROUP_CACHE_SIZE 10
48 #define DEFAULT_GROUP_CACHE_TTL 10
50 static pthread_mutex_t _group_cache_lock
= PTHREAD_MUTEX_INITIALIZER
;
51 static void *_group_cache
[GROUP_CACHE_SIZE
] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
52 static unsigned int _group_cache_best_before
[GROUP_CACHE_SIZE
] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
53 static unsigned int _group_cache_index
= 0;
54 static unsigned int _group_cache_ttl
= DEFAULT_GROUP_CACHE_TTL
;
56 static pthread_mutex_t _group_lock
= PTHREAD_MUTEX_INITIALIZER
;
63 free_group_data(struct group
*g
)
67 if (g
== NULL
) return;
69 if (g
->gr_name
!= NULL
) free(g
->gr_name
);
70 if (g
->gr_passwd
!= NULL
) free(g
->gr_passwd
);
75 while (*mem
!= NULL
) free(*mem
++);
81 free_group(struct group
*g
)
83 if (g
== NULL
) return;
89 free_lu_thread_info_group(void *x
)
91 struct lu_thread_info
*tdata
;
93 if (x
== NULL
) return;
95 tdata
= (struct lu_thread_info
*)x
;
97 if (tdata
->lu_entry
!= NULL
)
99 free_group((struct group
*)tdata
->lu_entry
);
100 tdata
->lu_entry
= NULL
;
103 _lu_data_free_vm_xdr(tdata
);
108 static struct group
*
109 extract_group(XDR
*xdr
)
111 int i
, j
, nkeys
, nvals
, status
;
115 if (xdr
== NULL
) return NULL
;
117 if (!xdr_int(xdr
, &nkeys
)) return NULL
;
119 g
= (struct group
*)calloc(1, sizeof(struct group
));
122 for (i
= 0; i
< nkeys
; i
++)
128 status
= _lu_xdr_attribute(xdr
, &key
, &vals
, &nvals
);
143 if ((g
->gr_name
== NULL
) && (!strcmp("name", key
)))
145 g
->gr_name
= vals
[0];
148 else if ((g
->gr_passwd
== NULL
) && (!strcmp("passwd", key
)))
150 g
->gr_passwd
= vals
[0];
153 else if ((g
->gr_gid
== (gid_t
)-2) && (!strcmp("gid", key
)))
155 g
->gr_gid
= atoi(vals
[0]);
156 if ((g
->gr_gid
== 0) && (strcmp(vals
[0], "0"))) g
->gr_gid
= -2;
158 else if ((g
->gr_mem
== NULL
) && (!strcmp("users", key
)))
168 for (; j
< nvals
; j
++) free(vals
[j
]);
173 if (g
->gr_name
== NULL
) g
->gr_name
= strdup("");
174 if (g
->gr_passwd
== NULL
) g
->gr_passwd
= strdup("");
175 if (g
->gr_mem
== NULL
) g
->gr_mem
= (char **)calloc(1, sizeof(char *));
180 static struct group
*
181 copy_group(struct group
*in
)
186 if (in
== NULL
) return NULL
;
188 g
= (struct group
*)calloc(1, sizeof(struct group
));
190 g
->gr_name
= LU_COPY_STRING(in
->gr_name
);
191 g
->gr_passwd
= LU_COPY_STRING(in
->gr_passwd
);
192 g
->gr_gid
= in
->gr_gid
;
195 if (in
->gr_mem
!= NULL
)
197 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++);
200 g
->gr_mem
= (char **)calloc(len
+ 1, sizeof(char *));
201 for (i
= 0; i
< len
; i
++)
203 g
->gr_mem
[i
] = strdup(in
->gr_mem
[i
]);
210 copy_group_r(struct group
*in
, struct group
*out
, char *buffer
, int buflen
)
216 if (in
== NULL
) return -1;
217 if (out
== NULL
) return -1;
219 if (buffer
== NULL
) buflen
= 0;
221 /* Calculate size of input */
223 if (in
->gr_name
!= NULL
) hsize
+= strlen(in
->gr_name
);
224 if (in
->gr_passwd
!= NULL
) hsize
+= strlen(in
->gr_passwd
);
226 /* NULL pointer at end of list */
227 hsize
+= sizeof(char *);
230 if (in
->gr_mem
!= NULL
)
232 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++)
234 hsize
+= sizeof(char *);
235 hsize
+= strlen(in
->gr_mem
[len
]);
239 /* Check buffer space */
240 if (hsize
> buflen
) return -1;
242 /* Copy result into caller's struct group, using buffer for memory */
246 if (in
->gr_name
!= NULL
)
249 hsize
= strlen(in
->gr_name
) + 1;
250 memmove(bp
, in
->gr_name
, hsize
);
254 out
->gr_passwd
= NULL
;
255 if (in
->gr_passwd
!= NULL
)
258 hsize
= strlen(in
->gr_passwd
) + 1;
259 memmove(bp
, in
->gr_passwd
, hsize
);
263 out
->gr_gid
= in
->gr_gid
;
266 ap
= bp
+ ((len
+ 1) * sizeof(char *));
268 if (in
->gr_mem
!= NULL
)
270 out
->gr_mem
= (char **)bp
;
271 for (i
= 0; i
< len
; i
++)
273 addr
= (unsigned long)ap
;
274 memmove(bp
, &addr
, sizeof(unsigned long));
275 bp
+= sizeof(unsigned long);
277 hsize
= strlen(in
->gr_mem
[i
]) + 1;
278 memmove(ap
, in
->gr_mem
[i
], hsize
);
283 memset(bp
, 0, sizeof(unsigned long));
290 recycle_group(struct lu_thread_info
*tdata
, struct group
*in
)
294 if (tdata
== NULL
) return;
295 g
= (struct group
*)tdata
->lu_entry
;
300 tdata
->lu_entry
= NULL
;
303 if (tdata
->lu_entry
== NULL
)
305 tdata
->lu_entry
= in
;
311 g
->gr_name
= in
->gr_name
;
312 g
->gr_passwd
= in
->gr_passwd
;
313 g
->gr_gid
= in
->gr_gid
;
314 g
->gr_mem
= in
->gr_mem
;
319 __private_extern__
unsigned int
320 get_group_cache_ttl()
322 return _group_cache_ttl
;
325 __private_extern__
void
326 set_group_cache_ttl(unsigned int ttl
)
330 pthread_mutex_lock(&_group_cache_lock
);
332 _group_cache_ttl
= ttl
;
336 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
338 if (_group_cache
[i
] == NULL
) continue;
340 free_group((struct group
*)_group_cache
[i
]);
341 _group_cache
[i
] = NULL
;
342 _group_cache_best_before
[i
] = 0;
346 pthread_mutex_unlock(&_group_cache_lock
);
350 cache_group(struct group
*gr
)
353 struct group
*grcache
;
355 if (_group_cache_ttl
== 0) return;
356 if (gr
== NULL
) return;
358 pthread_mutex_lock(&_group_cache_lock
);
360 grcache
= copy_group(gr
);
362 gettimeofday(&now
, NULL
);
364 if (_group_cache
[_group_cache_index
] != NULL
)
365 free_group((struct group
*)_group_cache
[_group_cache_index
]);
367 _group_cache
[_group_cache_index
] = grcache
;
368 _group_cache_best_before
[_group_cache_index
] = now
.tv_sec
+ _group_cache_ttl
;
369 _group_cache_index
= (_group_cache_index
+ 1) % GROUP_CACHE_SIZE
;
371 pthread_mutex_unlock(&_group_cache_lock
);
374 static struct group
*
375 cache_getgrnam(const char *name
)
378 struct group
*gr
, *res
;
381 if (_group_cache_ttl
== 0) return NULL
;
382 if (name
== NULL
) return NULL
;
384 pthread_mutex_lock(&_group_cache_lock
);
386 gettimeofday(&now
, NULL
);
388 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
390 if (_group_cache_best_before
[i
] == 0) continue;
391 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
393 gr
= (struct group
*)_group_cache
[i
];
395 if (gr
->gr_name
== NULL
) continue;
397 if (!strcmp(name
, gr
->gr_name
))
399 res
= copy_group(gr
);
400 pthread_mutex_unlock(&_group_cache_lock
);
405 pthread_mutex_unlock(&_group_cache_lock
);
409 static struct group
*
410 cache_getgrgid(int gid
)
413 struct group
*gr
, *res
;
416 if (_group_cache_ttl
== 0) return NULL
;
418 pthread_mutex_lock(&_group_cache_lock
);
420 gettimeofday(&now
, NULL
);
422 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
424 if (_group_cache_best_before
[i
] == 0) continue;
425 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
427 gr
= (struct group
*)_group_cache
[i
];
429 if ((gid_t
)gid
== gr
->gr_gid
)
431 res
= copy_group(gr
);
432 pthread_mutex_unlock(&_group_cache_lock
);
437 pthread_mutex_unlock(&_group_cache_lock
);
441 static struct group
*
445 unsigned int datalen
;
447 static int proc
= -1;
453 if (_lookup_link(_lu_port
, "getgrgid", &proc
) != KERN_SUCCESS
)
463 if (_lookup_all(_lu_port
, proc
, (unit
*)&gid
, 1, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
468 datalen
*= BYTES_PER_XDR_UNIT
;
469 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
471 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
474 if (!xdr_int(&inxdr
, &count
))
477 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
484 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
488 g
= extract_group(&inxdr
);
490 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
495 static struct group
*
496 lu_getgrnam(const char *name
)
499 unsigned int datalen
;
500 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
503 static int proc
= -1;
509 if (_lookup_link(_lu_port
, "getgrnam", &proc
) != KERN_SUCCESS
)
515 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
517 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
519 xdr_destroy(&outxdr
);
526 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
, xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
531 xdr_destroy(&outxdr
);
533 datalen
*= BYTES_PER_XDR_UNIT
;
534 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
536 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
539 if (!xdr_int(&inxdr
, &count
))
542 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
549 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
553 g
= extract_group(&inxdr
);
555 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
561 _old_getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
572 * When installing primary group, duplicate it;
573 * the first element of groups is the effective gid
574 * and will be overwritten when a setgid file is executed.
576 groups
[ngroups
++] = agroup
;
577 if (maxgroups
> 1) groups
[ngroups
++] = agroup
;
580 * Scan the group file to find additional groups.
584 while ((grp
= getgrent()))
586 if (grp
->gr_gid
== (gid_t
)agroup
) continue;
587 for (i
= 0; grp
->gr_mem
[i
]; i
++)
589 if (!strcmp(grp
->gr_mem
[i
], uname
))
591 if (ngroups
>= maxgroups
)
597 groups
[ngroups
++] = grp
->gr_gid
;
609 lu_getgrouplist(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
611 unsigned int datalen
;
614 static int proc
= -1;
616 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
621 if (groups
== NULL
) return -1;
622 if (*grpcnt
== 0) return -1;
625 groups
[ngroups
++] = basegid
;
626 if (*grpcnt
== 1) return 0;
630 /* getgrouplist duplicates the primary group! */
631 groups
[ngroups
++] = basegid
;
632 if (*grpcnt
== 2) return 0;
637 if (_lookup_link(_lu_port
, "initgroups", &proc
) != KERN_SUCCESS
)
643 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
644 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
646 xdr_destroy(&outxdr
);
653 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
,
654 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
657 xdr_destroy(&outxdr
);
661 xdr_destroy(&outxdr
);
663 datalen
*= BYTES_PER_XDR_UNIT
;
664 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
666 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
668 if (!xdr_int(&inxdr
, &count
))
671 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
675 for (i
= 0; i
< count
; i
++)
677 if (!xdr_int(&inxdr
, &a_group
)) break;
680 if (dupbase
!= 0) j
= 1;
681 for (; j
< ngroups
; j
++)
683 if (groups
[j
] == a_group
) break;
688 groups
[ngroups
++] = a_group
;
689 if (ngroups
== *grpcnt
) break;
694 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
701 getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
705 return lu_getgrouplist(uname
, agroup
, groups
, grpcnt
, 1);
708 return _old_getgrouplist(uname
, agroup
, groups
, grpcnt
);
712 lu_initgroups(const char *name
, int basegid
)
714 int status
, ngroups
, groups
[NGROUPS
];
717 status
= lu_getgrouplist(name
, basegid
, groups
, &ngroups
, 0);
718 if (status
< 0) return status
;
720 return setgroups(ngroups
, groups
);
726 struct lu_thread_info
*tdata
;
728 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
729 _lu_data_free_vm_xdr(tdata
);
739 static struct group
*
743 static int proc
= -1;
744 struct lu_thread_info
*tdata
;
746 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
749 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
750 _lu_data_set_key(_lu_data_key_group
, tdata
);
753 if (tdata
->lu_vm
== NULL
)
757 if (_lookup_link(_lu_port
, "getgrent", &proc
) != KERN_SUCCESS
)
764 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
770 /* mig stubs measure size in words (4 bytes) */
771 tdata
->lu_vm_length
*= 4;
773 if (tdata
->lu_xdr
!= NULL
)
775 xdr_destroy(tdata
->lu_xdr
);
778 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
780 xdrmem_create(tdata
->lu_xdr
, tdata
->lu_vm
, tdata
->lu_vm_length
, XDR_DECODE
);
781 if (!xdr_int(tdata
->lu_xdr
, &tdata
->lu_vm_cursor
))
788 if (tdata
->lu_vm_cursor
== 0)
794 g
= extract_group(tdata
->lu_xdr
);
801 tdata
->lu_vm_cursor
--;
806 static struct group
*
807 getgr_internal(const char *name
, gid_t gid
, int source
)
809 struct group
*res
= NULL
;
818 res
= cache_getgrnam(name
);
821 res
= cache_getgrgid(gid
);
830 else if (_lu_running())
835 res
= lu_getgrnam(name
);
838 res
= lu_getgrgid(gid
);
848 pthread_mutex_lock(&_group_lock
);
852 res
= copy_group(_old_getgrnam(name
));
855 res
= copy_group(_old_getgrgid(gid
));
858 res
= copy_group(_old_getgrent());
862 pthread_mutex_unlock(&_group_lock
);
865 if (from_cache
== 0) cache_group(res
);
870 static struct group
*
871 getgr(const char *name
, gid_t gid
, int source
)
873 struct group
*res
= NULL
;
874 struct lu_thread_info
*tdata
;
876 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
879 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
880 _lu_data_set_key(_lu_data_key_group
, tdata
);
883 res
= getgr_internal(name
, gid
, source
);
885 recycle_group(tdata
, res
);
886 return (struct group
*)tdata
->lu_entry
;
890 getgr_r(const char *name
, gid_t gid
, int source
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
892 struct group
*res
= NULL
;
898 res
= getgr_internal(name
, gid
, source
);
899 if (res
== NULL
) return -1;
901 status
= copy_group_r(res
, grp
, buffer
, bufsize
);
915 initgroups(const char *name
, int basegid
)
919 if (name
== NULL
) return -1;
923 if ((res
= lu_initgroups(name
, basegid
)))
925 res
= _old_initgroups(name
, basegid
);
930 res
= _old_initgroups(name
, basegid
);
937 getgrnam(const char *name
)
939 return getgr(name
, -2, GR_GET_NAME
);
945 return getgr(NULL
, gid
, GR_GET_GID
);
951 return getgr(NULL
, -2, GR_GET_ENT
);
957 if (_lu_running()) lu_setgrent();
958 else _old_setgrent();
965 if (_lu_running()) lu_endgrent();
966 else _old_endgrent();
970 getgrnam_r(const char *name
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
972 return getgr_r(name
, -2, GR_GET_NAME
, grp
, buffer
, bufsize
, result
);
976 getgrgid_r(gid_t gid
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
978 return getgr_r(NULL
, gid
, GR_GET_GID
, grp
, buffer
, bufsize
, result
);