xref: /netbsd-src/lib/libc/gen/getgroupmembership.c (revision ce099b40997c43048fb78bd578195f81d2456523)
1*ce099b40Smartin /*	$NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin Exp $	*/
2a3665ef9Slukem 
3a3665ef9Slukem /*-
4a3665ef9Slukem  * Copyright (c) 2004-2005 The NetBSD Foundation, Inc.
5a3665ef9Slukem  * All rights reserved.
6a3665ef9Slukem  *
7a3665ef9Slukem  * This code is derived from software contributed to The NetBSD Foundation
8a3665ef9Slukem  * by Luke Mewburn.
9a3665ef9Slukem  *
10a3665ef9Slukem  * Redistribution and use in source and binary forms, with or without
11a3665ef9Slukem  * modification, are permitted provided that the following conditions
12a3665ef9Slukem  * are met:
13a3665ef9Slukem  * 1. Redistributions of source code must retain the above copyright
14a3665ef9Slukem  *    notice, this list of conditions and the following disclaimer.
15a3665ef9Slukem  * 2. Redistributions in binary form must reproduce the above copyright
16a3665ef9Slukem  *    notice, this list of conditions and the following disclaimer in the
17a3665ef9Slukem  *    documentation and/or other materials provided with the distribution.
18a3665ef9Slukem  *
19a3665ef9Slukem  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20a3665ef9Slukem  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21a3665ef9Slukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22a3665ef9Slukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23a3665ef9Slukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24a3665ef9Slukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25a3665ef9Slukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26a3665ef9Slukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27a3665ef9Slukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28a3665ef9Slukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29a3665ef9Slukem  * POSSIBILITY OF SUCH DAMAGE.
30a3665ef9Slukem  */
31a3665ef9Slukem 
32a3665ef9Slukem #include <sys/cdefs.h>
33a3665ef9Slukem #if defined(LIBC_SCCS) && !defined(lint)
34*ce099b40Smartin __RCSID("$NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin Exp $");
35a3665ef9Slukem #endif /* LIBC_SCCS and not lint */
36a3665ef9Slukem 
37a3665ef9Slukem /*
38a3665ef9Slukem  * calculate group access list
39a3665ef9Slukem  */
40a3665ef9Slukem 
41a3665ef9Slukem #include "namespace.h"
42a3665ef9Slukem #include "reentrant.h"
43a3665ef9Slukem 
44a3665ef9Slukem #include <sys/param.h>
45a3665ef9Slukem 
46a3665ef9Slukem #include <assert.h>
47a3665ef9Slukem #include <errno.h>
48a3665ef9Slukem #include <grp.h>
49a3665ef9Slukem #include <limits.h>
50a3665ef9Slukem #include <nsswitch.h>
51a3665ef9Slukem #include <stdarg.h>
52a3665ef9Slukem #include <stdio.h>
53a3665ef9Slukem #include <stdlib.h>
54a3665ef9Slukem #include <string.h>
55a3665ef9Slukem #include <unistd.h>
56a3665ef9Slukem 
57a3665ef9Slukem #ifdef HESIOD
58a3665ef9Slukem #include <hesiod.h>
59a3665ef9Slukem #endif
60a3665ef9Slukem 
61a3665ef9Slukem #include "gr_private.h"
62a3665ef9Slukem 
63a3665ef9Slukem #ifdef __weak_alias
__weak_alias(getgroupmembership,_getgroupmembership)64a3665ef9Slukem __weak_alias(getgroupmembership,_getgroupmembership)
65a3665ef9Slukem #endif
66a3665ef9Slukem 
67a3665ef9Slukem /*
68a3665ef9Slukem  * __gr_addgid
69a3665ef9Slukem  *	Add gid to the groups array (of maxgrp size) at the position
70a3665ef9Slukem  *	indicated by *groupc, unless it already exists or *groupc is
71a3665ef9Slukem  *	past &groups[maxgrp].
72a3665ef9Slukem  *	Returns 1 upon success (including duplicate suppression), 0 otherwise.
73a3665ef9Slukem  */
74a3665ef9Slukem static int
75a3665ef9Slukem __gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *groupc)
76a3665ef9Slukem {
77a3665ef9Slukem 	int	ret, dupc;
78a3665ef9Slukem 
790e61db23Schristos 	_DIAGASSERT(groupc != NULL);
800e61db23Schristos 	_DIAGASSERT(groups != NULL);
81a3665ef9Slukem 
82a3665ef9Slukem 						/* skip duplicates */
83a3665ef9Slukem 	for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
84a3665ef9Slukem 		if (groups[dupc] == gid)
85a3665ef9Slukem 			return 1;
86a3665ef9Slukem 	}
87a3665ef9Slukem 
88a3665ef9Slukem 	ret = 1;
89a3665ef9Slukem 	if (*groupc < maxgrp)			/* add this gid */
90a3665ef9Slukem 		groups[*groupc] = gid;
91a3665ef9Slukem 	else
92a3665ef9Slukem 		ret = 0;
93a3665ef9Slukem 	(*groupc)++;
94a3665ef9Slukem 	return ret;
95a3665ef9Slukem }
96a3665ef9Slukem 
97a3665ef9Slukem 
98a3665ef9Slukem /*ARGSUSED*/
99a3665ef9Slukem static int
_files_getgroupmembership(void * retval,void * cb_data,va_list ap)100a3665ef9Slukem _files_getgroupmembership(void *retval, void *cb_data, va_list ap)
101a3665ef9Slukem {
102a3665ef9Slukem 	int		*result	= va_arg(ap, int *);
103a3665ef9Slukem 	const char 	*uname	= va_arg(ap, const char *);
104a3665ef9Slukem 	gid_t		 agroup	= va_arg(ap, gid_t);
105a3665ef9Slukem 	gid_t		*groups	= va_arg(ap, gid_t *);
106a3665ef9Slukem 	int		 maxgrp	= va_arg(ap, int);
107a3665ef9Slukem 	int		*groupc	= va_arg(ap, int *);
108a3665ef9Slukem 
109a3665ef9Slukem 	struct __grstate_files	state;
110a3665ef9Slukem 	struct group		grp;
111a3665ef9Slukem 	char			grpbuf[_GETGR_R_SIZE_MAX];
112a3665ef9Slukem 	int			rv, i;
113a3665ef9Slukem 
114a3665ef9Slukem 	_DIAGASSERT(result != NULL);
115a3665ef9Slukem 	_DIAGASSERT(uname != NULL);
116a3665ef9Slukem 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
117a3665ef9Slukem 	_DIAGASSERT(groupc != NULL);
118a3665ef9Slukem 
119a3665ef9Slukem 						/* install primary group */
120a3665ef9Slukem 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
121a3665ef9Slukem 
122a3665ef9Slukem 	memset(&state, 0, sizeof(state));
123a3665ef9Slukem 	while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
124a3665ef9Slukem 				0, NULL, 0) == NS_SUCCESS) {
125a3665ef9Slukem 						/* scan members */
126a3665ef9Slukem 		for (i = 0; grp.gr_mem[i]; i++) {
127a3665ef9Slukem 			if (strcmp(grp.gr_mem[i], uname) != 0)
128a3665ef9Slukem 				continue;
129a3665ef9Slukem 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
130a3665ef9Slukem 				*result = -1;
131a3665ef9Slukem 			break;
132a3665ef9Slukem 		}
133a3665ef9Slukem 	}
134a3665ef9Slukem 	__grend_files(&state);
135a3665ef9Slukem 	return NS_NOTFOUND;
136a3665ef9Slukem }
137a3665ef9Slukem 
138a3665ef9Slukem 
139a3665ef9Slukem #ifdef HESIOD
140a3665ef9Slukem 
141a3665ef9Slukem /*ARGSUSED*/
142a3665ef9Slukem static int
_dns_getgroupmembership(void * retval,void * cb_data,va_list ap)143a3665ef9Slukem _dns_getgroupmembership(void *retval, void *cb_data, va_list ap)
144a3665ef9Slukem {
145a3665ef9Slukem 	int		*result	= va_arg(ap, int *);
146a3665ef9Slukem 	const char 	*uname	= va_arg(ap, const char *);
147a3665ef9Slukem 	gid_t		 agroup	= va_arg(ap, gid_t);
148a3665ef9Slukem 	gid_t		*groups	= va_arg(ap, gid_t *);
149a3665ef9Slukem 	int		 maxgrp	= va_arg(ap, int);
150a3665ef9Slukem 	int		*groupc	= va_arg(ap, int *);
151a3665ef9Slukem 
152a3665ef9Slukem 	struct __grstate_dns	state;
153a3665ef9Slukem 	struct group		grp;
154a3665ef9Slukem 	char			grpbuf[_GETGR_R_SIZE_MAX];
155a3665ef9Slukem 	unsigned long		id;
156a3665ef9Slukem 	void			*context;
157a3665ef9Slukem 	char			**hp, *cp, *ep;
158a3665ef9Slukem 	int			rv, i;
159a3665ef9Slukem 
160a3665ef9Slukem 	_DIAGASSERT(result != NULL);
161a3665ef9Slukem 	_DIAGASSERT(uname != NULL);
162a3665ef9Slukem 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
163a3665ef9Slukem 	_DIAGASSERT(groupc != NULL);
164a3665ef9Slukem 
165a3665ef9Slukem 						/* install primary group */
166a3665ef9Slukem 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
167a3665ef9Slukem 
168a3665ef9Slukem 	hp = NULL;
169a3665ef9Slukem 	rv = NS_NOTFOUND;
170a3665ef9Slukem 
171a3665ef9Slukem 	if (hesiod_init(&context) == -1)		/* setup hesiod */
172a3665ef9Slukem 		return NS_UNAVAIL;
173a3665ef9Slukem 
174a3665ef9Slukem 	hp = hesiod_resolve(context, uname, "grplist");	/* find grplist */
175a3665ef9Slukem 	if (hp == NULL) {
176a3665ef9Slukem 		if (errno != ENOENT) {			/* wasn't "not found"*/
177a3665ef9Slukem 			rv = NS_UNAVAIL;
178a3665ef9Slukem 			goto dnsgroupmembers_out;
179a3665ef9Slukem 		}
180a3665ef9Slukem 			/* grplist not found, fallback to _dns_grscan */
181a3665ef9Slukem 		memset(&state, 0, sizeof(state));
182a3665ef9Slukem 		while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
183a3665ef9Slukem 					0, NULL, 0) == NS_SUCCESS) {
184a3665ef9Slukem 							/* scan members */
185a3665ef9Slukem 			for (i = 0; grp.gr_mem[i]; i++) {
186a3665ef9Slukem 				if (strcmp(grp.gr_mem[i], uname) != 0)
187a3665ef9Slukem 					continue;
188a3665ef9Slukem 				if (! __gr_addgid(grp.gr_gid, groups, maxgrp,
189a3665ef9Slukem 				    groupc))
190a3665ef9Slukem 					*result = -1;
191a3665ef9Slukem 				break;
192a3665ef9Slukem 			}
193a3665ef9Slukem 		}
194a3665ef9Slukem 		__grend_dns(&state);
195a3665ef9Slukem 		rv = NS_NOTFOUND;
196a3665ef9Slukem 		goto dnsgroupmembers_out;
197a3665ef9Slukem 	}
198a3665ef9Slukem 
199a3665ef9Slukem 	if ((ep = strchr(hp[0], '\n')) != NULL)
200a3665ef9Slukem 		*ep = '\0';				/* clear trailing \n */
201a3665ef9Slukem 
202a3665ef9Slukem 	for (cp = hp[0]; *cp != '\0'; ) {		/* parse grplist */
203a3665ef9Slukem 		if ((cp = strchr(cp, ':')) == NULL)	/* skip grpname */
204a3665ef9Slukem 			break;
205a3665ef9Slukem 		cp++;
206a3665ef9Slukem 		id = strtoul(cp, &ep, 10);		/* parse gid */
207a3665ef9Slukem 		if (id > GID_MAX || (*ep != ':' && *ep != '\0')) {
208a3665ef9Slukem 			rv = NS_UNAVAIL;
209a3665ef9Slukem 			goto dnsgroupmembers_out;
210a3665ef9Slukem 		}
211a3665ef9Slukem 		cp = ep;
212a3665ef9Slukem 		if (*cp == ':')
213a3665ef9Slukem 			cp++;
214a3665ef9Slukem 
215a3665ef9Slukem 							/* add gid */
216a3665ef9Slukem 		if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc))
217a3665ef9Slukem 			*result = -1;
218a3665ef9Slukem 	}
219a3665ef9Slukem 
220a3665ef9Slukem 	rv = NS_NOTFOUND;
221a3665ef9Slukem 
222a3665ef9Slukem  dnsgroupmembers_out:
223a3665ef9Slukem 	if (hp)
224a3665ef9Slukem 		hesiod_free_list(context, hp);
225a3665ef9Slukem 	hesiod_end(context);
226a3665ef9Slukem 	return rv;
227a3665ef9Slukem }
228a3665ef9Slukem 
229a3665ef9Slukem #endif /* HESIOD */
230a3665ef9Slukem 
231a3665ef9Slukem 
232a3665ef9Slukem #ifdef YP
233a3665ef9Slukem 
234a3665ef9Slukem /*ARGSUSED*/
235a3665ef9Slukem static int
_nis_getgroupmembership(void * retval,void * cb_data,va_list ap)236a3665ef9Slukem _nis_getgroupmembership(void *retval, void *cb_data, va_list ap)
237a3665ef9Slukem {
238a3665ef9Slukem 	int		*result	= va_arg(ap, int *);
239a3665ef9Slukem 	const char 	*uname	= va_arg(ap, const char *);
240a3665ef9Slukem 	gid_t		 agroup	= va_arg(ap, gid_t);
241a3665ef9Slukem 	gid_t		*groups	= va_arg(ap, gid_t *);
242a3665ef9Slukem 	int		 maxgrp	= va_arg(ap, int);
243a3665ef9Slukem 	int		*groupc	= va_arg(ap, int *);
244a3665ef9Slukem 
245a3665ef9Slukem 	struct __grstate_nis	state;
246a3665ef9Slukem 	struct group		grp;
247a3665ef9Slukem 	char			grpbuf[_GETGR_R_SIZE_MAX];
248a3665ef9Slukem 	int			rv, i;
249a3665ef9Slukem 
250a3665ef9Slukem 	_DIAGASSERT(result != NULL);
251a3665ef9Slukem 	_DIAGASSERT(uname != NULL);
252a3665ef9Slukem 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
253a3665ef9Slukem 	_DIAGASSERT(groupc != NULL);
254a3665ef9Slukem 
255a3665ef9Slukem 						/* install primary group */
256a3665ef9Slukem 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
257a3665ef9Slukem 
258a3665ef9Slukem 	memset(&state, 0, sizeof(state));
259a3665ef9Slukem 	while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
260a3665ef9Slukem 				0, NULL, 0) == NS_SUCCESS) {
261a3665ef9Slukem 						/* scan members */
262a3665ef9Slukem 		for (i = 0; grp.gr_mem[i]; i++) {
263a3665ef9Slukem 			if (strcmp(grp.gr_mem[i], uname) != 0)
264a3665ef9Slukem 				continue;
265a3665ef9Slukem 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
266a3665ef9Slukem 				*result = -1;
267a3665ef9Slukem 			break;
268a3665ef9Slukem 		}
269a3665ef9Slukem 	}
270a3665ef9Slukem 	__grend_nis(&state);
271a3665ef9Slukem 
272a3665ef9Slukem 	return NS_NOTFOUND;
273a3665ef9Slukem }
274a3665ef9Slukem 
275a3665ef9Slukem #endif /* YP */
276a3665ef9Slukem 
277a3665ef9Slukem 
278a3665ef9Slukem #ifdef _GROUP_COMPAT
279a3665ef9Slukem 
280a3665ef9Slukem struct __compatggm {
281a3665ef9Slukem 	const char	*uname;		/* user to search for */
282a3665ef9Slukem 	gid_t		*groups;
283a3665ef9Slukem 	gid_t		 agroup;
284a3665ef9Slukem 	int		 maxgrp;
285a3665ef9Slukem 	int		*groupc;
286a3665ef9Slukem };
287a3665ef9Slukem 
288a3665ef9Slukem static int
_compat_ggm_search(void * cookie,struct group ** groupres)289a3665ef9Slukem _compat_ggm_search(void *cookie, struct group **groupres)
290a3665ef9Slukem {
291a3665ef9Slukem 	struct __compatggm	*cp;
292a3665ef9Slukem 	int			rerror, crv;
293a3665ef9Slukem 
294a3665ef9Slukem 	static const ns_dtab dtab[] = {
295a3665ef9Slukem 		NS_FILES_CB(__grbad_compat, "files")
296a3665ef9Slukem 		NS_DNS_CB(_dns_getgroupmembership, NULL)
297a3665ef9Slukem 		NS_NIS_CB(_nis_getgroupmembership, NULL)
298a3665ef9Slukem 		NS_COMPAT_CB(__grbad_compat, "compat")
299ce2c90c7Schristos 		NS_NULL_CB
300a3665ef9Slukem 	};
301a3665ef9Slukem 
302a3665ef9Slukem 	*groupres = NULL;	/* we don't care about this */
303a3665ef9Slukem 	cp = (struct __compatggm *)cookie;
304a3665ef9Slukem 
305a3665ef9Slukem 	crv = nsdispatch(NULL, dtab,
306a3665ef9Slukem 	    NSDB_GROUP_COMPAT, "getgroupmembership",
307a3665ef9Slukem 	    __nsdefaultnis,
308a3665ef9Slukem 	    &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc);
309a3665ef9Slukem 
310a3665ef9Slukem 	if (crv == NS_SUCCESS)
311a3665ef9Slukem 		crv = NS_NOTFOUND;	/* indicate "no more +: entries" */
312a3665ef9Slukem 
313a3665ef9Slukem 	return crv;
314a3665ef9Slukem }
315a3665ef9Slukem 
316a3665ef9Slukem /* ARGSUSED */
317a3665ef9Slukem static int
_compat_getgroupmembership(void * retval,void * cb_data,va_list ap)318a3665ef9Slukem _compat_getgroupmembership(void *retval, void *cb_data, va_list ap)
319a3665ef9Slukem {
320a3665ef9Slukem 	int		*result	= va_arg(ap, int *);
321a3665ef9Slukem 	const char 	*uname	= va_arg(ap, const char *);
322a3665ef9Slukem 	gid_t		 agroup	= va_arg(ap, gid_t);
323a3665ef9Slukem 	gid_t		*groups	= va_arg(ap, gid_t *);
324a3665ef9Slukem 	int		 maxgrp	= va_arg(ap, int);
325a3665ef9Slukem 	int		*groupc	= va_arg(ap, int *);
326a3665ef9Slukem 
327a3665ef9Slukem 	struct __grstate_compat	state;
328a3665ef9Slukem 	struct __compatggm	ggmstate;
329a3665ef9Slukem 	struct group		grp;
330a3665ef9Slukem 	char			grpbuf[_GETGR_R_SIZE_MAX];
331a3665ef9Slukem 	int			rv, i;
332a3665ef9Slukem 
333a3665ef9Slukem 	_DIAGASSERT(result != NULL);
334a3665ef9Slukem 	_DIAGASSERT(uname != NULL);
335a3665ef9Slukem 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
336a3665ef9Slukem 	_DIAGASSERT(groupc != NULL);
337a3665ef9Slukem 
338a3665ef9Slukem 						/* install primary group */
339a3665ef9Slukem 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
340a3665ef9Slukem 
341a3665ef9Slukem 	memset(&state, 0, sizeof(state));
342a3665ef9Slukem 	memset(&ggmstate, 0, sizeof(ggmstate));
343a3665ef9Slukem 	ggmstate.uname = uname;
344a3665ef9Slukem 	ggmstate.groups = groups;
345a3665ef9Slukem 	ggmstate.agroup = agroup;
346a3665ef9Slukem 	ggmstate.maxgrp = maxgrp;
347a3665ef9Slukem 	ggmstate.groupc = groupc;
348a3665ef9Slukem 
349a3665ef9Slukem 	while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
350a3665ef9Slukem 				0, NULL, 0, _compat_ggm_search, &ggmstate)
351a3665ef9Slukem 		== NS_SUCCESS) {
352a3665ef9Slukem 						/* scan members */
353a3665ef9Slukem 		for (i = 0; grp.gr_mem[i]; i++) {
354a3665ef9Slukem 			if (strcmp(grp.gr_mem[i], uname) != 0)
355a3665ef9Slukem 				continue;
356a3665ef9Slukem 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
357a3665ef9Slukem 				*result = -1;
358a3665ef9Slukem 			break;
359a3665ef9Slukem 		}
360a3665ef9Slukem 	}
361a3665ef9Slukem 
362a3665ef9Slukem 	__grend_compat(&state);
363a3665ef9Slukem 	return NS_NOTFOUND;
364a3665ef9Slukem }
365a3665ef9Slukem 
366a3665ef9Slukem #endif	/* _GROUP_COMPAT */
367a3665ef9Slukem 
368a3665ef9Slukem 
369a3665ef9Slukem int
getgroupmembership(const char * uname,gid_t agroup,gid_t * groups,int maxgrp,int * groupc)370a3665ef9Slukem getgroupmembership(const char *uname, gid_t agroup,
371a3665ef9Slukem     gid_t *groups, int maxgrp, int *groupc)
372a3665ef9Slukem {
373a3665ef9Slukem 	int	rerror;
374a3665ef9Slukem 
375a3665ef9Slukem 	static const ns_dtab dtab[] = {
376a3665ef9Slukem 		NS_FILES_CB(_files_getgroupmembership, NULL)
377a3665ef9Slukem 		NS_DNS_CB(_dns_getgroupmembership, NULL)
378a3665ef9Slukem 		NS_NIS_CB(_nis_getgroupmembership, NULL)
379a3665ef9Slukem 		NS_COMPAT_CB(_compat_getgroupmembership, NULL)
380ce2c90c7Schristos 		NS_NULL_CB
381a3665ef9Slukem 	};
382a3665ef9Slukem 
383a3665ef9Slukem 	_DIAGASSERT(uname != NULL);
384a3665ef9Slukem 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
385a3665ef9Slukem 	_DIAGASSERT(groupc != NULL);
386a3665ef9Slukem 
387a3665ef9Slukem 	*groupc = 0;
388a3665ef9Slukem 
389a3665ef9Slukem 	mutex_lock(&__grmutex);
390a3665ef9Slukem 			/*
391a3665ef9Slukem 			 * Call each backend.
392a3665ef9Slukem 			 * For compatibility with getgrent(3) semantics,
393a3665ef9Slukem 			 * a backend should return NS_NOTFOUND even upon
394a3665ef9Slukem 			 * completion, to allow result merging to occur.
395a3665ef9Slukem 			 */
396a3665ef9Slukem 	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
397a3665ef9Slukem 	    __nsdefaultcompat,
398a3665ef9Slukem 	    &rerror, uname, agroup, groups, maxgrp, groupc);
399a3665ef9Slukem 	mutex_unlock(&__grmutex);
400a3665ef9Slukem 
401a3665ef9Slukem 	if (*groupc > maxgrp)			/* too many groups found */
402a3665ef9Slukem 		return -1;
403a3665ef9Slukem 	else
404a3665ef9Slukem 		return 0;
405a3665ef9Slukem }
406