2 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 * Copyright (C) 1989 by NeXT, Inc.
31 #include <mach/mach.h>
35 #include <rpc/types.h>
38 #include <netinet/in.h>
39 #include <sys/param.h>
43 #include "_lu_types.h"
46 #include "lu_overrides.h"
48 #define GROUP_CACHE_SIZE 10
49 #define DEFAULT_GROUP_CACHE_TTL 10
51 static pthread_mutex_t _group_cache_lock
= PTHREAD_MUTEX_INITIALIZER
;
52 static void *_group_cache
[GROUP_CACHE_SIZE
] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
53 static unsigned int _group_cache_best_before
[GROUP_CACHE_SIZE
] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
54 static unsigned int _group_cache_index
= 0;
55 static unsigned int _group_cache_ttl
= DEFAULT_GROUP_CACHE_TTL
;
57 static pthread_mutex_t _group_lock
= PTHREAD_MUTEX_INITIALIZER
;
64 free_group_data(struct group
*g
)
68 if (g
== NULL
) return;
70 if (g
->gr_name
!= NULL
) free(g
->gr_name
);
71 if (g
->gr_passwd
!= NULL
) free(g
->gr_passwd
);
76 while (*mem
!= NULL
) free(*mem
++);
82 free_group(struct group
*g
)
84 if (g
== NULL
) return;
90 free_lu_thread_info_group(void *x
)
92 struct lu_thread_info
*tdata
;
94 if (x
== NULL
) return;
96 tdata
= (struct lu_thread_info
*)x
;
98 if (tdata
->lu_entry
!= NULL
)
100 free_group((struct group
*)tdata
->lu_entry
);
101 tdata
->lu_entry
= NULL
;
104 _lu_data_free_vm_xdr(tdata
);
109 static struct group
*
110 extract_group(XDR
*xdr
)
112 int i
, j
, nkeys
, nvals
, status
;
116 if (xdr
== NULL
) return NULL
;
118 if (!xdr_int(xdr
, &nkeys
)) return NULL
;
120 g
= (struct group
*)calloc(1, sizeof(struct group
));
123 for (i
= 0; i
< nkeys
; i
++)
129 status
= _lu_xdr_attribute(xdr
, &key
, &vals
, &nvals
);
144 if ((g
->gr_name
== NULL
) && (!strcmp("name", key
)))
146 g
->gr_name
= vals
[0];
149 else if ((g
->gr_passwd
== NULL
) && (!strcmp("passwd", key
)))
151 g
->gr_passwd
= vals
[0];
154 else if ((g
->gr_gid
== (gid_t
)-2) && (!strcmp("gid", key
)))
156 g
->gr_gid
= atoi(vals
[0]);
157 if ((g
->gr_gid
== 0) && (strcmp(vals
[0], "0"))) g
->gr_gid
= -2;
159 else if ((g
->gr_mem
== NULL
) && (!strcmp("users", key
)))
169 for (; j
< nvals
; j
++) free(vals
[j
]);
174 if (g
->gr_name
== NULL
) g
->gr_name
= strdup("");
175 if (g
->gr_passwd
== NULL
) g
->gr_passwd
= strdup("");
176 if (g
->gr_mem
== NULL
) g
->gr_mem
= (char **)calloc(1, sizeof(char *));
181 static struct group
*
182 copy_group(struct group
*in
)
187 if (in
== NULL
) return NULL
;
189 g
= (struct group
*)calloc(1, sizeof(struct group
));
191 g
->gr_name
= LU_COPY_STRING(in
->gr_name
);
192 g
->gr_passwd
= LU_COPY_STRING(in
->gr_passwd
);
193 g
->gr_gid
= in
->gr_gid
;
196 if (in
->gr_mem
!= NULL
)
198 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++);
201 g
->gr_mem
= (char **)calloc(len
+ 1, sizeof(char *));
202 for (i
= 0; i
< len
; i
++)
204 g
->gr_mem
[i
] = strdup(in
->gr_mem
[i
]);
211 copy_group_r(struct group
*in
, struct group
*out
, char *buffer
, int buflen
)
217 if (in
== NULL
) return -1;
218 if (out
== NULL
) return -1;
220 if (buffer
== NULL
) buflen
= 0;
222 /* Calculate size of input */
224 if (in
->gr_name
!= NULL
) hsize
+= strlen(in
->gr_name
);
225 if (in
->gr_passwd
!= NULL
) hsize
+= strlen(in
->gr_passwd
);
227 /* NULL pointer at end of list */
228 hsize
+= sizeof(char *);
231 if (in
->gr_mem
!= NULL
)
233 for (len
= 0; in
->gr_mem
[len
] != NULL
; len
++)
235 hsize
+= sizeof(char *);
236 hsize
+= strlen(in
->gr_mem
[len
]);
240 /* Check buffer space */
241 if (hsize
> buflen
) return -1;
243 /* Copy result into caller's struct group, using buffer for memory */
247 if (in
->gr_name
!= NULL
)
250 hsize
= strlen(in
->gr_name
) + 1;
251 memmove(bp
, in
->gr_name
, hsize
);
255 out
->gr_passwd
= NULL
;
256 if (in
->gr_passwd
!= NULL
)
259 hsize
= strlen(in
->gr_passwd
) + 1;
260 memmove(bp
, in
->gr_passwd
, hsize
);
264 out
->gr_gid
= in
->gr_gid
;
267 ap
= bp
+ ((len
+ 1) * sizeof(char *));
269 if (in
->gr_mem
!= NULL
)
271 out
->gr_mem
= (char **)bp
;
272 for (i
= 0; i
< len
; i
++)
274 addr
= (unsigned long)ap
;
275 memmove(bp
, &addr
, sizeof(unsigned long));
276 bp
+= sizeof(unsigned long);
278 hsize
= strlen(in
->gr_mem
[i
]) + 1;
279 memmove(ap
, in
->gr_mem
[i
], hsize
);
284 memset(bp
, 0, sizeof(unsigned long));
291 recycle_group(struct lu_thread_info
*tdata
, struct group
*in
)
295 if (tdata
== NULL
) return;
296 g
= (struct group
*)tdata
->lu_entry
;
301 tdata
->lu_entry
= NULL
;
304 if (tdata
->lu_entry
== NULL
)
306 tdata
->lu_entry
= in
;
312 g
->gr_name
= in
->gr_name
;
313 g
->gr_passwd
= in
->gr_passwd
;
314 g
->gr_gid
= in
->gr_gid
;
315 g
->gr_mem
= in
->gr_mem
;
320 __private_extern__
unsigned int
321 get_group_cache_ttl()
323 return _group_cache_ttl
;
326 __private_extern__
void
327 set_group_cache_ttl(unsigned int ttl
)
331 pthread_mutex_lock(&_group_cache_lock
);
333 _group_cache_ttl
= ttl
;
337 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
339 if (_group_cache
[i
] == NULL
) continue;
341 free_group((struct group
*)_group_cache
[i
]);
342 _group_cache
[i
] = NULL
;
343 _group_cache_best_before
[i
] = 0;
347 pthread_mutex_unlock(&_group_cache_lock
);
351 cache_group(struct group
*gr
)
354 struct group
*grcache
;
356 if (_group_cache_ttl
== 0) return;
357 if (gr
== NULL
) return;
359 pthread_mutex_lock(&_group_cache_lock
);
361 grcache
= copy_group(gr
);
363 gettimeofday(&now
, NULL
);
365 if (_group_cache
[_group_cache_index
] != NULL
)
366 free_group((struct group
*)_group_cache
[_group_cache_index
]);
368 _group_cache
[_group_cache_index
] = grcache
;
369 _group_cache_best_before
[_group_cache_index
] = now
.tv_sec
+ _group_cache_ttl
;
370 _group_cache_index
= (_group_cache_index
+ 1) % GROUP_CACHE_SIZE
;
372 pthread_mutex_unlock(&_group_cache_lock
);
375 static struct group
*
376 cache_getgrnam(const char *name
)
379 struct group
*gr
, *res
;
382 if (_group_cache_ttl
== 0) return NULL
;
383 if (name
== NULL
) return NULL
;
385 pthread_mutex_lock(&_group_cache_lock
);
387 gettimeofday(&now
, NULL
);
389 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
391 if (_group_cache_best_before
[i
] == 0) continue;
392 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
394 gr
= (struct group
*)_group_cache
[i
];
396 if (gr
->gr_name
== NULL
) continue;
398 if (!strcmp(name
, gr
->gr_name
))
400 res
= copy_group(gr
);
401 pthread_mutex_unlock(&_group_cache_lock
);
406 pthread_mutex_unlock(&_group_cache_lock
);
410 static struct group
*
411 cache_getgrgid(int gid
)
414 struct group
*gr
, *res
;
417 if (_group_cache_ttl
== 0) return NULL
;
419 pthread_mutex_lock(&_group_cache_lock
);
421 gettimeofday(&now
, NULL
);
423 for (i
= 0; i
< GROUP_CACHE_SIZE
; i
++)
425 if (_group_cache_best_before
[i
] == 0) continue;
426 if ((unsigned int)now
.tv_sec
> _group_cache_best_before
[i
]) continue;
428 gr
= (struct group
*)_group_cache
[i
];
430 if ((gid_t
)gid
== gr
->gr_gid
)
432 res
= copy_group(gr
);
433 pthread_mutex_unlock(&_group_cache_lock
);
438 pthread_mutex_unlock(&_group_cache_lock
);
442 static struct group
*
446 unsigned int datalen
;
448 static int proc
= -1;
454 if (_lookup_link(_lu_port
, "getgrgid", &proc
) != KERN_SUCCESS
)
464 if (_lookup_all(_lu_port
, proc
, (unit
*)&gid
, 1, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
469 datalen
*= BYTES_PER_XDR_UNIT
;
470 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
472 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
475 if (!xdr_int(&inxdr
, &count
))
478 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
485 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
489 g
= extract_group(&inxdr
);
491 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
496 static struct group
*
497 lu_getgrnam(const char *name
)
500 unsigned int datalen
;
501 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
504 static int proc
= -1;
510 if (_lookup_link(_lu_port
, "getgrnam", &proc
) != KERN_SUCCESS
)
516 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
518 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
520 xdr_destroy(&outxdr
);
527 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
, xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
) != KERN_SUCCESS
)
532 xdr_destroy(&outxdr
);
534 datalen
*= BYTES_PER_XDR_UNIT
;
535 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
537 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
540 if (!xdr_int(&inxdr
, &count
))
543 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
550 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
554 g
= extract_group(&inxdr
);
556 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
562 _old_getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
573 * When installing primary group, duplicate it;
574 * the first element of groups is the effective gid
575 * and will be overwritten when a setgid file is executed.
577 groups
[ngroups
++] = agroup
;
578 if (maxgroups
> 1) groups
[ngroups
++] = agroup
;
581 * Scan the group file to find additional groups.
585 while ((grp
= getgrent()))
587 if (grp
->gr_gid
== (gid_t
)agroup
) continue;
588 for (i
= 0; grp
->gr_mem
[i
]; i
++)
590 if (!strcmp(grp
->gr_mem
[i
], uname
))
592 if (ngroups
>= maxgroups
)
598 groups
[ngroups
++] = grp
->gr_gid
;
610 lu_getgrouplist(const char *name
, int basegid
, int *groups
, int *grpcnt
, int dupbase
)
612 unsigned int datalen
;
615 static int proc
= -1;
617 char namebuf
[_LU_MAXLUSTRLEN
+ BYTES_PER_XDR_UNIT
];
622 if (groups
== NULL
) return -1;
623 if (*grpcnt
== 0) return -1;
626 groups
[ngroups
++] = basegid
;
627 if (*grpcnt
== 1) return 0;
631 /* getgrouplist duplicates the primary group! */
632 groups
[ngroups
++] = basegid
;
633 if (*grpcnt
== 2) return 0;
638 if (_lookup_link(_lu_port
, "initgroups", &proc
) != KERN_SUCCESS
)
644 xdrmem_create(&outxdr
, namebuf
, sizeof(namebuf
), XDR_ENCODE
);
645 if (!xdr__lu_string(&outxdr
, (_lu_string
*)&name
))
647 xdr_destroy(&outxdr
);
654 if (_lookup_all(_lu_port
, proc
, (unit
*)namebuf
,
655 xdr_getpos(&outxdr
) / BYTES_PER_XDR_UNIT
, &lookup_buf
, &datalen
)
658 xdr_destroy(&outxdr
);
662 xdr_destroy(&outxdr
);
664 datalen
*= BYTES_PER_XDR_UNIT
;
665 if ((lookup_buf
== NULL
) || (datalen
== 0)) return NULL
;
667 xdrmem_create(&inxdr
, lookup_buf
, datalen
, XDR_DECODE
);
669 if (!xdr_int(&inxdr
, &count
))
672 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
676 for (i
= 0; i
< count
; i
++)
678 if (!xdr_int(&inxdr
, &a_group
)) break;
681 if (dupbase
!= 0) j
= 1;
682 for (; j
< ngroups
; j
++)
684 if (groups
[j
] == a_group
) break;
689 groups
[ngroups
++] = a_group
;
690 if (ngroups
== *grpcnt
) break;
695 vm_deallocate(mach_task_self(), (vm_address_t
)lookup_buf
, datalen
);
702 getgrouplist(const char *uname
, int agroup
, int *groups
, int *grpcnt
)
706 return lu_getgrouplist(uname
, agroup
, groups
, grpcnt
, 1);
709 return _old_getgrouplist(uname
, agroup
, groups
, grpcnt
);
713 lu_initgroups(const char *name
, int basegid
)
715 int status
, ngroups
, groups
[NGROUPS
];
718 status
= lu_getgrouplist(name
, basegid
, groups
, &ngroups
, 0);
719 if (status
< 0) return status
;
721 return setgroups(ngroups
, groups
);
727 struct lu_thread_info
*tdata
;
729 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
730 _lu_data_free_vm_xdr(tdata
);
740 static struct group
*
744 static int proc
= -1;
745 struct lu_thread_info
*tdata
;
747 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
750 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
751 _lu_data_set_key(_lu_data_key_group
, tdata
);
754 if (tdata
->lu_vm
== NULL
)
758 if (_lookup_link(_lu_port
, "getgrent", &proc
) != KERN_SUCCESS
)
765 if (_lookup_all(_lu_port
, proc
, NULL
, 0, &(tdata
->lu_vm
), &(tdata
->lu_vm_length
)) != KERN_SUCCESS
)
771 /* mig stubs measure size in words (4 bytes) */
772 tdata
->lu_vm_length
*= 4;
774 if (tdata
->lu_xdr
!= NULL
)
776 xdr_destroy(tdata
->lu_xdr
);
779 tdata
->lu_xdr
= (XDR
*)calloc(1, sizeof(XDR
));
781 xdrmem_create(tdata
->lu_xdr
, tdata
->lu_vm
, tdata
->lu_vm_length
, XDR_DECODE
);
782 if (!xdr_int(tdata
->lu_xdr
, &tdata
->lu_vm_cursor
))
789 if (tdata
->lu_vm_cursor
== 0)
795 g
= extract_group(tdata
->lu_xdr
);
802 tdata
->lu_vm_cursor
--;
807 static struct group
*
808 getgr_internal(const char *name
, gid_t gid
, int source
)
810 struct group
*res
= NULL
;
819 res
= cache_getgrnam(name
);
822 res
= cache_getgrgid(gid
);
831 else if (_lu_running())
836 res
= lu_getgrnam(name
);
839 res
= lu_getgrgid(gid
);
849 pthread_mutex_lock(&_group_lock
);
853 res
= copy_group(_old_getgrnam(name
));
856 res
= copy_group(_old_getgrgid(gid
));
859 res
= copy_group(_old_getgrent());
863 pthread_mutex_unlock(&_group_lock
);
866 if (from_cache
== 0) cache_group(res
);
871 static struct group
*
872 getgr(const char *name
, gid_t gid
, int source
)
874 struct group
*res
= NULL
;
875 struct lu_thread_info
*tdata
;
877 tdata
= _lu_data_create_key(_lu_data_key_group
, free_lu_thread_info_group
);
880 tdata
= (struct lu_thread_info
*)calloc(1, sizeof(struct lu_thread_info
));
881 _lu_data_set_key(_lu_data_key_group
, tdata
);
884 res
= getgr_internal(name
, gid
, source
);
886 recycle_group(tdata
, res
);
887 return (struct group
*)tdata
->lu_entry
;
891 getgr_r(const char *name
, gid_t gid
, int source
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
893 struct group
*res
= NULL
;
899 res
= getgr_internal(name
, gid
, source
);
900 if (res
== NULL
) return -1;
902 status
= copy_group_r(res
, grp
, buffer
, bufsize
);
916 initgroups(const char *name
, int basegid
)
920 if (name
== NULL
) return -1;
924 if ((res
= lu_initgroups(name
, basegid
)))
926 res
= _old_initgroups(name
, basegid
);
931 res
= _old_initgroups(name
, basegid
);
938 getgrnam(const char *name
)
940 return getgr(name
, -2, GR_GET_NAME
);
946 return getgr(NULL
, gid
, GR_GET_GID
);
952 return getgr(NULL
, -2, GR_GET_ENT
);
958 if (_lu_running()) lu_setgrent();
959 else _old_setgrent();
966 if (_lu_running()) lu_endgrent();
967 else _old_endgrent();
971 getgrnam_r(const char *name
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
973 return getgr_r(name
, -2, GR_GET_NAME
, grp
, buffer
, bufsize
, result
);
977 getgrgid_r(gid_t gid
, struct group
*grp
, char *buffer
, size_t bufsize
, struct group
**result
)
979 return getgr_r(NULL
, gid
, GR_GET_GID
, grp
, buffer
, bufsize
, result
);