Home | History | Annotate | Line # | Download | only in gen
      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