Home | History | Annotate | Line # | Download | only in gen
getgroupmembership.c revision 1.1
      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