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