/* $NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin 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.
*
* 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 <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: getgroupmembership.c,v 1.4 2008/04/28 20:22:59 martin Exp $");
#endif /* LIBC_SCCS and not lint */
/*
* __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;
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;
}
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;
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);
_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;
}