Home | History | Annotate | Line # | Download | only in gen
getgroupmembership.c revision 1.3
      1 /*	$NetBSD: getgroupmembership.c,v 1.3 2007/02/03 16:17:15 christos 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.3 2007/02/03 16:17:15 christos 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(groupc != NULL);
     87 	_DIAGASSERT(groups != NULL);
     88 
     89 						/* skip duplicates */
     90 	for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
     91 		if (groups[dupc] == gid)
     92 			return 1;
     93 	}
     94 
     95 	ret = 1;
     96 	if (*groupc < maxgrp)			/* add this gid */
     97 		groups[*groupc] = gid;
     98 	else
     99 		ret = 0;
    100 	(*groupc)++;
    101 	return ret;
    102 }
    103 
    104 
    105 /*ARGSUSED*/
    106 static int
    107 _files_getgroupmembership(void *retval, void *cb_data, va_list ap)
    108 {
    109 	int		*result	= va_arg(ap, int *);
    110 	const char 	*uname	= va_arg(ap, const char *);
    111 	gid_t		 agroup	= va_arg(ap, gid_t);
    112 	gid_t		*groups	= va_arg(ap, gid_t *);
    113 	int		 maxgrp	= va_arg(ap, int);
    114 	int		*groupc	= va_arg(ap, int *);
    115 
    116 	struct __grstate_files	state;
    117 	struct group		grp;
    118 	char			grpbuf[_GETGR_R_SIZE_MAX];
    119 	int			rv, i;
    120 
    121 	_DIAGASSERT(result != NULL);
    122 	_DIAGASSERT(uname != NULL);
    123 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
    124 	_DIAGASSERT(groupc != NULL);
    125 
    126 						/* install primary group */
    127 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
    128 
    129 	memset(&state, 0, sizeof(state));
    130 	while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
    131 				0, NULL, 0) == NS_SUCCESS) {
    132 						/* scan members */
    133 		for (i = 0; grp.gr_mem[i]; i++) {
    134 			if (strcmp(grp.gr_mem[i], uname) != 0)
    135 				continue;
    136 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
    137 				*result = -1;
    138 			break;
    139 		}
    140 	}
    141 	__grend_files(&state);
    142 	return NS_NOTFOUND;
    143 }
    144 
    145 
    146 #ifdef HESIOD
    147 
    148 /*ARGSUSED*/
    149 static int
    150 _dns_getgroupmembership(void *retval, void *cb_data, va_list ap)
    151 {
    152 	int		*result	= va_arg(ap, int *);
    153 	const char 	*uname	= va_arg(ap, const char *);
    154 	gid_t		 agroup	= va_arg(ap, gid_t);
    155 	gid_t		*groups	= va_arg(ap, gid_t *);
    156 	int		 maxgrp	= va_arg(ap, int);
    157 	int		*groupc	= va_arg(ap, int *);
    158 
    159 	struct __grstate_dns	state;
    160 	struct group		grp;
    161 	char			grpbuf[_GETGR_R_SIZE_MAX];
    162 	unsigned long		id;
    163 	void			*context;
    164 	char			**hp, *cp, *ep;
    165 	int			rv, i;
    166 
    167 	_DIAGASSERT(result != NULL);
    168 	_DIAGASSERT(uname != NULL);
    169 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
    170 	_DIAGASSERT(groupc != NULL);
    171 
    172 						/* install primary group */
    173 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
    174 
    175 	hp = NULL;
    176 	rv = NS_NOTFOUND;
    177 
    178 	if (hesiod_init(&context) == -1)		/* setup hesiod */
    179 		return NS_UNAVAIL;
    180 
    181 	hp = hesiod_resolve(context, uname, "grplist");	/* find grplist */
    182 	if (hp == NULL) {
    183 		if (errno != ENOENT) {			/* wasn't "not found"*/
    184 			rv = NS_UNAVAIL;
    185 			goto dnsgroupmembers_out;
    186 		}
    187 			/* grplist not found, fallback to _dns_grscan */
    188 		memset(&state, 0, sizeof(state));
    189 		while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
    190 					0, NULL, 0) == NS_SUCCESS) {
    191 							/* scan members */
    192 			for (i = 0; grp.gr_mem[i]; i++) {
    193 				if (strcmp(grp.gr_mem[i], uname) != 0)
    194 					continue;
    195 				if (! __gr_addgid(grp.gr_gid, groups, maxgrp,
    196 				    groupc))
    197 					*result = -1;
    198 				break;
    199 			}
    200 		}
    201 		__grend_dns(&state);
    202 		rv = NS_NOTFOUND;
    203 		goto dnsgroupmembers_out;
    204 	}
    205 
    206 	if ((ep = strchr(hp[0], '\n')) != NULL)
    207 		*ep = '\0';				/* clear trailing \n */
    208 
    209 	for (cp = hp[0]; *cp != '\0'; ) {		/* parse grplist */
    210 		if ((cp = strchr(cp, ':')) == NULL)	/* skip grpname */
    211 			break;
    212 		cp++;
    213 		id = strtoul(cp, &ep, 10);		/* parse gid */
    214 		if (id > GID_MAX || (*ep != ':' && *ep != '\0')) {
    215 			rv = NS_UNAVAIL;
    216 			goto dnsgroupmembers_out;
    217 		}
    218 		cp = ep;
    219 		if (*cp == ':')
    220 			cp++;
    221 
    222 							/* add gid */
    223 		if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc))
    224 			*result = -1;
    225 	}
    226 
    227 	rv = NS_NOTFOUND;
    228 
    229  dnsgroupmembers_out:
    230 	if (hp)
    231 		hesiod_free_list(context, hp);
    232 	hesiod_end(context);
    233 	return rv;
    234 }
    235 
    236 #endif /* HESIOD */
    237 
    238 
    239 #ifdef YP
    240 
    241 /*ARGSUSED*/
    242 static int
    243 _nis_getgroupmembership(void *retval, void *cb_data, va_list ap)
    244 {
    245 	int		*result	= va_arg(ap, int *);
    246 	const char 	*uname	= va_arg(ap, const char *);
    247 	gid_t		 agroup	= va_arg(ap, gid_t);
    248 	gid_t		*groups	= va_arg(ap, gid_t *);
    249 	int		 maxgrp	= va_arg(ap, int);
    250 	int		*groupc	= va_arg(ap, int *);
    251 
    252 	struct __grstate_nis	state;
    253 	struct group		grp;
    254 	char			grpbuf[_GETGR_R_SIZE_MAX];
    255 	int			rv, i;
    256 
    257 	_DIAGASSERT(result != NULL);
    258 	_DIAGASSERT(uname != NULL);
    259 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
    260 	_DIAGASSERT(groupc != NULL);
    261 
    262 						/* install primary group */
    263 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
    264 
    265 	memset(&state, 0, sizeof(state));
    266 	while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
    267 				0, NULL, 0) == NS_SUCCESS) {
    268 						/* scan members */
    269 		for (i = 0; grp.gr_mem[i]; i++) {
    270 			if (strcmp(grp.gr_mem[i], uname) != 0)
    271 				continue;
    272 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
    273 				*result = -1;
    274 			break;
    275 		}
    276 	}
    277 	__grend_nis(&state);
    278 
    279 	return NS_NOTFOUND;
    280 }
    281 
    282 #endif /* YP */
    283 
    284 
    285 #ifdef _GROUP_COMPAT
    286 
    287 struct __compatggm {
    288 	const char	*uname;		/* user to search for */
    289 	gid_t		*groups;
    290 	gid_t		 agroup;
    291 	int		 maxgrp;
    292 	int		*groupc;
    293 };
    294 
    295 static int
    296 _compat_ggm_search(void *cookie, struct group **groupres)
    297 {
    298 	struct __compatggm	*cp;
    299 	int			rerror, crv;
    300 
    301 	static const ns_dtab dtab[] = {
    302 		NS_FILES_CB(__grbad_compat, "files")
    303 		NS_DNS_CB(_dns_getgroupmembership, NULL)
    304 		NS_NIS_CB(_nis_getgroupmembership, NULL)
    305 		NS_COMPAT_CB(__grbad_compat, "compat")
    306 		NS_NULL_CB
    307 	};
    308 
    309 	*groupres = NULL;	/* we don't care about this */
    310 	cp = (struct __compatggm *)cookie;
    311 
    312 	crv = nsdispatch(NULL, dtab,
    313 	    NSDB_GROUP_COMPAT, "getgroupmembership",
    314 	    __nsdefaultnis,
    315 	    &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc);
    316 
    317 	if (crv == NS_SUCCESS)
    318 		crv = NS_NOTFOUND;	/* indicate "no more +: entries" */
    319 
    320 	return crv;
    321 }
    322 
    323 /* ARGSUSED */
    324 static int
    325 _compat_getgroupmembership(void *retval, void *cb_data, va_list ap)
    326 {
    327 	int		*result	= va_arg(ap, int *);
    328 	const char 	*uname	= va_arg(ap, const char *);
    329 	gid_t		 agroup	= va_arg(ap, gid_t);
    330 	gid_t		*groups	= va_arg(ap, gid_t *);
    331 	int		 maxgrp	= va_arg(ap, int);
    332 	int		*groupc	= va_arg(ap, int *);
    333 
    334 	struct __grstate_compat	state;
    335 	struct __compatggm	ggmstate;
    336 	struct group		grp;
    337 	char			grpbuf[_GETGR_R_SIZE_MAX];
    338 	int			rv, i;
    339 
    340 	_DIAGASSERT(result != NULL);
    341 	_DIAGASSERT(uname != NULL);
    342 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
    343 	_DIAGASSERT(groupc != NULL);
    344 
    345 						/* install primary group */
    346 	(void) __gr_addgid(agroup, groups, maxgrp, groupc);
    347 
    348 	memset(&state, 0, sizeof(state));
    349 	memset(&ggmstate, 0, sizeof(ggmstate));
    350 	ggmstate.uname = uname;
    351 	ggmstate.groups = groups;
    352 	ggmstate.agroup = agroup;
    353 	ggmstate.maxgrp = maxgrp;
    354 	ggmstate.groupc = groupc;
    355 
    356 	while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
    357 				0, NULL, 0, _compat_ggm_search, &ggmstate)
    358 		== NS_SUCCESS) {
    359 						/* scan members */
    360 		for (i = 0; grp.gr_mem[i]; i++) {
    361 			if (strcmp(grp.gr_mem[i], uname) != 0)
    362 				continue;
    363 			if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
    364 				*result = -1;
    365 			break;
    366 		}
    367 	}
    368 
    369 	__grend_compat(&state);
    370 	return NS_NOTFOUND;
    371 }
    372 
    373 #endif	/* _GROUP_COMPAT */
    374 
    375 
    376 int
    377 getgroupmembership(const char *uname, gid_t agroup,
    378     gid_t *groups, int maxgrp, int *groupc)
    379 {
    380 	int	rerror;
    381 
    382 	static const ns_dtab dtab[] = {
    383 		NS_FILES_CB(_files_getgroupmembership, NULL)
    384 		NS_DNS_CB(_dns_getgroupmembership, NULL)
    385 		NS_NIS_CB(_nis_getgroupmembership, NULL)
    386 		NS_COMPAT_CB(_compat_getgroupmembership, NULL)
    387 		NS_NULL_CB
    388 	};
    389 
    390 	_DIAGASSERT(uname != NULL);
    391 	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
    392 	_DIAGASSERT(groupc != NULL);
    393 
    394 	*groupc = 0;
    395 
    396 	mutex_lock(&__grmutex);
    397 			/*
    398 			 * Call each backend.
    399 			 * For compatibility with getgrent(3) semantics,
    400 			 * a backend should return NS_NOTFOUND even upon
    401 			 * completion, to allow result merging to occur.
    402 			 */
    403 	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
    404 	    __nsdefaultcompat,
    405 	    &rerror, uname, agroup, groups, maxgrp, groupc);
    406 	mutex_unlock(&__grmutex);
    407 
    408 	if (*groupc > maxgrp)			/* too many groups found */
    409 		return -1;
    410 	else
    411 		return 0;
    412 }
    413