/* $NetBSD: getgroupmembership.c,v 1.3 2007/02/03 16:17:15 christos Exp $ */ /*- * Copyright (c) 2004-2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: getgroupmembership.c,v 1.3 2007/02/03 16:17:15 christos Exp $"); #endif /* LIBC_SCCS and not lint */ /* * calculate group access list */ #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HESIOD #include #endif #include "gr_private.h" #ifdef __weak_alias __weak_alias(getgroupmembership,_getgroupmembership) #endif /* * __gr_addgid * Add gid to the groups array (of maxgrp size) at the position * indicated by *groupc, unless it already exists or *groupc is * past &groups[maxgrp]. * Returns 1 upon success (including duplicate suppression), 0 otherwise. */ static int __gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *groupc) { int ret, dupc; _DIAGASSERT(groupc != NULL); _DIAGASSERT(groups != NULL); /* skip duplicates */ for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) { if (groups[dupc] == gid) return 1; } ret = 1; if (*groupc < maxgrp) /* add this gid */ groups[*groupc] = gid; else ret = 0; (*groupc)++; return ret; } /*ARGSUSED*/ static int _files_getgroupmembership(void *retval, void *cb_data, va_list ap) { int *result = va_arg(ap, int *); const char *uname = va_arg(ap, const char *); gid_t agroup = va_arg(ap, gid_t); gid_t *groups = va_arg(ap, gid_t *); int maxgrp = va_arg(ap, int); int *groupc = va_arg(ap, int *); struct __grstate_files state; struct group grp; char grpbuf[_GETGR_R_SIZE_MAX]; int rv, i; _DIAGASSERT(result != NULL); _DIAGASSERT(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ _DIAGASSERT(groupc != NULL); /* install primary group */ (void) __gr_addgid(agroup, groups, maxgrp, groupc); memset(&state, 0, sizeof(state)); while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state, 0, NULL, 0) == NS_SUCCESS) { /* scan members */ for (i = 0; grp.gr_mem[i]; i++) { if (strcmp(grp.gr_mem[i], uname) != 0) continue; if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc)) *result = -1; break; } } __grend_files(&state); return NS_NOTFOUND; } #ifdef HESIOD /*ARGSUSED*/ static int _dns_getgroupmembership(void *retval, void *cb_data, va_list ap) { int *result = va_arg(ap, int *); const char *uname = va_arg(ap, const char *); gid_t agroup = va_arg(ap, gid_t); gid_t *groups = va_arg(ap, gid_t *); int maxgrp = va_arg(ap, int); int *groupc = va_arg(ap, int *); struct __grstate_dns state; struct group grp; char grpbuf[_GETGR_R_SIZE_MAX]; unsigned long id; void *context; char **hp, *cp, *ep; int rv, i; _DIAGASSERT(result != NULL); _DIAGASSERT(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ _DIAGASSERT(groupc != NULL); /* install primary group */ (void) __gr_addgid(agroup, groups, maxgrp, groupc); hp = NULL; rv = NS_NOTFOUND; if (hesiod_init(&context) == -1) /* setup hesiod */ return NS_UNAVAIL; hp = hesiod_resolve(context, uname, "grplist"); /* find grplist */ if (hp == NULL) { if (errno != ENOENT) { /* wasn't "not found"*/ rv = NS_UNAVAIL; goto dnsgroupmembers_out; } /* grplist not found, fallback to _dns_grscan */ memset(&state, 0, sizeof(state)); while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state, 0, NULL, 0) == NS_SUCCESS) { /* scan members */ for (i = 0; grp.gr_mem[i]; i++) { if (strcmp(grp.gr_mem[i], uname) != 0) continue; if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc)) *result = -1; break; } } __grend_dns(&state); rv = NS_NOTFOUND; goto dnsgroupmembers_out; } if ((ep = strchr(hp[0], '\n')) != NULL) *ep = '\0'; /* clear trailing \n */ for (cp = hp[0]; *cp != '\0'; ) { /* parse grplist */ if ((cp = strchr(cp, ':')) == NULL) /* skip grpname */ break; cp++; id = strtoul(cp, &ep, 10); /* parse gid */ if (id > GID_MAX || (*ep != ':' && *ep != '\0')) { rv = NS_UNAVAIL; goto dnsgroupmembers_out; } cp = ep; if (*cp == ':') cp++; /* add gid */ if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc)) *result = -1; } rv = NS_NOTFOUND; dnsgroupmembers_out: if (hp) hesiod_free_list(context, hp); hesiod_end(context); return rv; } #endif /* HESIOD */ #ifdef YP /*ARGSUSED*/ static int _nis_getgroupmembership(void *retval, void *cb_data, va_list ap) { int *result = va_arg(ap, int *); const char *uname = va_arg(ap, const char *); gid_t agroup = va_arg(ap, gid_t); gid_t *groups = va_arg(ap, gid_t *); int maxgrp = va_arg(ap, int); int *groupc = va_arg(ap, int *); struct __grstate_nis state; struct group grp; char grpbuf[_GETGR_R_SIZE_MAX]; int rv, i; _DIAGASSERT(result != NULL); _DIAGASSERT(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ _DIAGASSERT(groupc != NULL); /* install primary group */ (void) __gr_addgid(agroup, groups, maxgrp, groupc); memset(&state, 0, sizeof(state)); while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state, 0, NULL, 0) == NS_SUCCESS) { /* scan members */ for (i = 0; grp.gr_mem[i]; i++) { if (strcmp(grp.gr_mem[i], uname) != 0) continue; if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc)) *result = -1; break; } } __grend_nis(&state); return NS_NOTFOUND; } #endif /* YP */ #ifdef _GROUP_COMPAT struct __compatggm { const char *uname; /* user to search for */ gid_t *groups; gid_t agroup; int maxgrp; int *groupc; }; static int _compat_ggm_search(void *cookie, struct group **groupres) { struct __compatggm *cp; int rerror, crv; static const ns_dtab dtab[] = { NS_FILES_CB(__grbad_compat, "files") NS_DNS_CB(_dns_getgroupmembership, NULL) NS_NIS_CB(_nis_getgroupmembership, NULL) NS_COMPAT_CB(__grbad_compat, "compat") NS_NULL_CB }; *groupres = NULL; /* we don't care about this */ cp = (struct __compatggm *)cookie; crv = nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "getgroupmembership", __nsdefaultnis, &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc); if (crv == NS_SUCCESS) crv = NS_NOTFOUND; /* indicate "no more +: entries" */ return crv; } /* ARGSUSED */ static int _compat_getgroupmembership(void *retval, void *cb_data, va_list ap) { int *result = va_arg(ap, int *); const char *uname = va_arg(ap, const char *); gid_t agroup = va_arg(ap, gid_t); gid_t *groups = va_arg(ap, gid_t *); int maxgrp = va_arg(ap, int); int *groupc = va_arg(ap, int *); struct __grstate_compat state; struct __compatggm ggmstate; struct group grp; char grpbuf[_GETGR_R_SIZE_MAX]; int rv, i; _DIAGASSERT(result != NULL); _DIAGASSERT(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ _DIAGASSERT(groupc != NULL); /* install primary group */ (void) __gr_addgid(agroup, groups, maxgrp, groupc); memset(&state, 0, sizeof(state)); memset(&ggmstate, 0, sizeof(ggmstate)); ggmstate.uname = uname; ggmstate.groups = groups; ggmstate.agroup = agroup; ggmstate.maxgrp = maxgrp; ggmstate.groupc = groupc; while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state, 0, NULL, 0, _compat_ggm_search, &ggmstate) == NS_SUCCESS) { /* scan members */ for (i = 0; grp.gr_mem[i]; i++) { if (strcmp(grp.gr_mem[i], uname) != 0) continue; if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc)) *result = -1; break; } } __grend_compat(&state); return NS_NOTFOUND; } #endif /* _GROUP_COMPAT */ int getgroupmembership(const char *uname, gid_t agroup, gid_t *groups, int maxgrp, int *groupc) { int rerror; static const ns_dtab dtab[] = { NS_FILES_CB(_files_getgroupmembership, NULL) NS_DNS_CB(_dns_getgroupmembership, NULL) NS_NIS_CB(_nis_getgroupmembership, NULL) NS_COMPAT_CB(_compat_getgroupmembership, NULL) NS_NULL_CB }; _DIAGASSERT(uname != NULL); /* groups may be NULL if just sizing when invoked with maxgrp = 0 */ _DIAGASSERT(groupc != NULL); *groupc = 0; mutex_lock(&__grmutex); /* * Call each backend. * For compatibility with getgrent(3) semantics, * a backend should return NS_NOTFOUND even upon * completion, to allow result merging to occur. */ (void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership", __nsdefaultcompat, &rerror, uname, agroup, groups, maxgrp, groupc); mutex_unlock(&__grmutex); if (*groupc > maxgrp) /* too many groups found */ return -1; else return 0; }