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