#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
#else
__RCSID("$NetBSD: getgrent.c,v 1.68 2026/04/19 19:37:01 rillig Exp $");
#endif
#endif
#include "namespace.h"
#include "reentrant.h"
#include <sys/param.h>
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <nsswitch.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#ifdef HESIOD
#include <hesiod.h>
#endif
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
#include "gr_private.h"
#ifdef __weak_alias
__weak_alias(endgrent,_endgrent)
__weak_alias(getgrent,_getgrent)
__weak_alias(getgrent_r,_getgrent_r)
__weak_alias(getgrgid,_getgrgid)
__weak_alias(getgrgid_r,_getgrgid_r)
__weak_alias(getgrnam,_getgrnam)
__weak_alias(getgrnam_r,_getgrnam_r)
__weak_alias(setgrent,_setgrent)
__weak_alias(setgroupent,_setgroupent)
#endif
#ifdef _REENTRANT
mutex_t __grmutex = MUTEX_INITIALIZER;
#endif
static char *
_gr_memfrombuf(size_t want, char **buffer, size_t *buflen)
{
char *rv;
if (want > *buflen) {
errno = ERANGE;
return NULL;
}
rv = *buffer;
*buffer += want;
*buflen -= want;
return rv;
}
static int
_gr_parse(const char *entry, struct group *grp, char *buf, size_t buflen)
{
unsigned long id;
const char *bp;
char *ep;
size_t count;
int memc;
_DIAGASSERT(entry != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buf != NULL);
#define COPYTOBUF(to) \
do { \
(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
if ((to) == NULL) \
return 0; \
memmove((to), entry, count); \
to[count] = '\0'; \
} while (0)
#if 0
if (*entry == '+')
return 0;
#endif
count = strcspn(entry, ":");
if (entry[count] == '\0')
return 0;
COPYTOBUF(grp->gr_name);
entry += count + 1;
count = strcspn(entry, ":");
if (entry[count] == '\0')
return 0;
COPYTOBUF(grp->gr_passwd);
entry += count + 1;
count = strcspn(entry, ":");
if (entry[count] == '\0')
return 0;
id = strtoul(entry, &ep, 10);
if (id > GID_MAX || *ep != ':')
return 0;
grp->gr_gid = (gid_t)id;
entry += count + 1;
memc = 1;
if (*entry != '\0')
memc++;
for (bp = entry; *bp != '\0'; bp++) {
if (*bp == ',')
memc++;
}
ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
if (ep == NULL)
return 0;
grp->gr_mem = (char **)ALIGN(ep);
for (memc = 0; *entry != '\0'; memc++) {
count = strcspn(entry, ",");
COPYTOBUF(grp->gr_mem[memc]);
entry += count;
if (*entry == ',')
entry++;
}
#undef COPYTOBUF
grp->gr_mem[memc] = NULL;
return 1;
}
static int
_gr_copy(struct group *fromgrp, struct group *grp, char *buf, size_t buflen)
{
char *ep;
int memc;
_DIAGASSERT(fromgrp != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buf != NULL);
#define COPYSTR(to, from) \
do { \
size_t count = strlen((from)); \
(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
if ((to) == NULL) \
return 0; \
memmove((to), (from), count); \
to[count] = '\0'; \
} while (0)
COPYSTR(grp->gr_name, fromgrp->gr_name);
COPYSTR(grp->gr_passwd, fromgrp->gr_passwd);
grp->gr_gid = fromgrp->gr_gid;
if (fromgrp->gr_mem == NULL)
return 0;
for (memc = 0; fromgrp->gr_mem[memc]; memc++)
continue;
memc++;
ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
grp->gr_mem = (char **)ALIGN(ep);
if (grp->gr_mem == NULL)
return 0;
for (memc = 0; fromgrp->gr_mem[memc]; memc++) {
COPYSTR(grp->gr_mem[memc], fromgrp->gr_mem[memc]);
}
#undef COPYSTR
grp->gr_mem[memc] = NULL;
return 1;
}
int
__grstart_files(struct __grstate_files *state)
{
_DIAGASSERT(state != NULL);
if (state->fp == NULL) {
state->fp = fopen(_PATH_GROUP, "re");
if (state->fp == NULL)
return NS_UNAVAIL;
} else {
rewind(state->fp);
}
return NS_SUCCESS;
}
int
__grend_files(struct __grstate_files *state)
{
_DIAGASSERT(state != NULL);
if (state->fp) {
(void) fclose(state->fp);
state->fp = NULL;
}
return NS_SUCCESS;
}
int
__grscan_files(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_files *state, int search, const char *name, gid_t gid)
{
int rv;
char filebuf[_GETGR_R_SIZE_MAX], *ep;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
*retval = 0;
if (state->fp == NULL) {
rv = __grstart_files(state);
if (rv != NS_SUCCESS)
goto filesgrscan_out;
}
rv = NS_NOTFOUND;
while (fgets(filebuf, (int)sizeof(filebuf), state->fp) != NULL) {
ep = strchr(filebuf, '\n');
if (ep == NULL) {
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
continue;
}
*ep = '\0';
if (filebuf[0] == '+')
continue;
if (! _gr_parse(filebuf, grp, buffer, buflen)) {
continue;
}
if (! search) {
rv = NS_SUCCESS;
break;
}
if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
break;
}
}
filesgrscan_out:
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
return rv;
}
static struct __grstate_files _files_state;
static struct group _files_group;
static char _files_groupbuf[_GETGR_R_SIZE_MAX];
static int
_files_setgrent(void *nsrv, void *nscb, va_list ap)
{
_files_state.stayopen = 0;
return __grstart_files(&_files_state);
}
static int
_files_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_files_state.stayopen = stayopen;
rv = __grstart_files(&_files_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
static int
_files_endgrent(void *nsrv, void *nscb, va_list ap)
{
_files_state.stayopen = 0;
return __grend_files(&_files_state);
}
static int
_files_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grscan_files(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
static int
_files_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_files(retval, grp, buffer, buflen,
&_files_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
return rv;
}
static int
_files_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_files(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_files(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, NULL, gid);
if (!_files_state.stayopen)
__grend_files(&_files_state);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
static int
_files_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_files state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_files(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
static int
_files_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_files(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_files(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, name, 0);
if (!_files_state.stayopen)
__grend_files(&_files_state);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
static int
_files_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_files state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_files(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#ifdef HESIOD
int
__grstart_dns(struct __grstate_dns *state)
{
_DIAGASSERT(state != NULL);
state->num = 0;
if (state->context == NULL) {
if (hesiod_init(&state->context) == -1)
return NS_UNAVAIL;
}
return NS_SUCCESS;
}
int
__grend_dns(struct __grstate_dns *state)
{
_DIAGASSERT(state != NULL);
state->num = 0;
if (state->context) {
hesiod_end(state->context);
state->context = NULL;
}
return NS_SUCCESS;
}
int
__grscan_dns(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_dns *state, int search, const char *name, gid_t gid)
{
const char **curzone;
char **hp, *ep;
int rv;
static const char *zones_gid_group[] = {
"gid",
"group",
NULL
};
static const char *zones_group[] = {
"group",
NULL
};
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
*retval = 0;
if (state->context == NULL) {
rv = __grstart_dns(state);
if (rv != NS_SUCCESS)
return rv;
}
next_dns_entry:
hp = NULL;
rv = NS_NOTFOUND;
if (! search) {
if (state->num == -1)
return NS_NOTFOUND;
snprintf(buffer, buflen, "group-%u", state->num);
state->num++;
curzone = zones_group;
} else if (name) {
snprintf(buffer, buflen, "%s", name);
curzone = zones_group;
} else {
snprintf(buffer, buflen, "%u", (unsigned int)gid);
curzone = zones_gid_group;
}
for (; *curzone; curzone++) {
hp = hesiod_resolve(state->context, buffer, *curzone);
if (hp != NULL)
break;
if (errno != ENOENT) {
rv = NS_UNAVAIL;
goto dnsgrscan_out;
}
}
if (*curzone == NULL) {
if (! search)
state->num = -1;
goto dnsgrscan_out;
}
if ((ep = strchr(hp[0], '\n')) != NULL)
*ep = '\0';
if (_gr_parse(hp[0], grp, buffer, buflen)) {
if (! search) {
rv = NS_SUCCESS;
} else if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
}
} else {
if (!search) {
hesiod_free_list(state->context, hp);
goto next_dns_entry;
}
}
dnsgrscan_out:
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
if (hp)
hesiod_free_list(state->context, hp);
return rv;
}
static struct __grstate_dns _dns_state;
static struct group _dns_group;
static char _dns_groupbuf[_GETGR_R_SIZE_MAX];
static int
_dns_setgrent(void *nsrv, void *nscb, va_list ap)
{
_dns_state.stayopen = 0;
return __grstart_dns(&_dns_state);
}
static int
_dns_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_dns_state.stayopen = stayopen;
rv = __grstart_dns(&_dns_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
static int
_dns_endgrent(void *nsrv, void *nscb, va_list ap)
{
_dns_state.stayopen = 0;
return __grend_dns(&_dns_state);
}
static int
_dns_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_dns_group;
return rv;
}
static int
_dns_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_dns(retval, grp, buffer, buflen,
&_dns_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
return rv;
}
static int
_dns_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_dns(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, NULL, gid);
if (!_dns_state.stayopen)
__grend_dns(&_dns_state);
if (rv == NS_SUCCESS)
*retval = &_dns_group;
return rv;
}
static int
_dns_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_dns state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_dns(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
static int
_dns_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_dns(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, name, 0);
if (!_dns_state.stayopen)
__grend_dns(&_dns_state);
if (rv == NS_SUCCESS)
*retval = &_dns_group;
return rv;
}
static int
_dns_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_dns state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_dns(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif
#ifdef YP
int
__grstart_nis(struct __grstate_nis *state)
{
_DIAGASSERT(state != NULL);
state->done = 0;
if (state->current) {
free(state->current);
state->current = NULL;
}
if (state->domain == NULL) {
switch (yp_get_default_domain(&state->domain)) {
case 0:
break;
case YPERR_RESRC:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
}
return NS_SUCCESS;
}
int
__grend_nis(struct __grstate_nis *state)
{
_DIAGASSERT(state != NULL);
if (state->domain) {
state->domain = NULL;
}
state->done = 0;
if (state->current) {
free(state->current);
state->current = NULL;
}
return NS_SUCCESS;
}
int
__grscan_nis(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_nis *state, int search, const char *name, gid_t gid)
{
const char *map;
char *key, *data;
int nisr, rv, keylen, datalen;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
*retval = 0;
if (state->domain == NULL) {
rv = __grstart_nis(state);
if (rv != NS_SUCCESS)
return rv;
}
next_nis_entry:
key = NULL;
data = NULL;
rv = NS_SUCCESS;
if (! search) {
if (state->done)
return NS_NOTFOUND;
map = "group.byname";
if (state->current) {
nisr = yp_next(state->domain, map,
state->current, state->currentlen,
&key, &keylen, &data, &datalen);
free(state->current);
state->current = NULL;
switch (nisr) {
case 0:
state->current = key;
state->currentlen = keylen;
key = NULL;
break;
case YPERR_NOMORE:
rv = NS_NOTFOUND;
state->done = 1;
break;
default:
rv = NS_UNAVAIL;
break;
}
} else {
if (yp_first(state->domain, map,
&state->current, &state->currentlen,
&data, &datalen)) {
rv = NS_UNAVAIL;
}
}
} else {
if (name) {
snprintf(buffer, buflen, "%s", name);
map = "group.byname";
} else {
snprintf(buffer, buflen, "%u", (unsigned int)gid);
map = "group.bygid";
}
nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
&data, &datalen);
switch (nisr) {
case 0:
break;
case YPERR_KEY:
rv = NS_NOTFOUND;
break;
default:
rv = NS_UNAVAIL;
break;
}
}
if (rv == NS_SUCCESS) {
data[datalen] = '\0';
if (_gr_parse(data, grp, buffer, buflen)) {
if (! search) {
rv = NS_SUCCESS;
} else if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
}
} else {
if (!search) {
free(data);
goto next_nis_entry;
}
}
}
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
if (key)
free(key);
if (data)
free(data);
return rv;
}
static struct __grstate_nis _nis_state;
static struct group _nis_group;
static char _nis_groupbuf[_GETGR_R_SIZE_MAX];
static int
_nis_setgrent(void *nsrv, void *nscb, va_list ap)
{
_nis_state.stayopen = 0;
return __grstart_nis(&_nis_state);
}
static int
_nis_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_nis_state.stayopen = stayopen;
rv = __grstart_nis(&_nis_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
static int
_nis_endgrent(void *nsrv, void *nscb, va_list ap)
{
return __grend_nis(&_nis_state);
}
static int
_nis_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_nis_group;
return rv;
}
static int
_nis_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_nis(retval, grp, buffer, buflen,
&_nis_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
return rv;
}
static int
_nis_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_nis(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, NULL, gid);
if (!_nis_state.stayopen)
__grend_nis(&_nis_state);
if (rv == NS_SUCCESS)
*retval = &_nis_group;
return rv;
}
static int
_nis_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_nis state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
if (_nis_state.stayopen)
{
rv = __grscan_nis(retval, grp, buffer, buflen, &_nis_state, 1, NULL, gid);
}
else
{
memset(&state, 0, sizeof(state));
rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_nis(&state);
}
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
static int
_nis_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_nis(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, name, 0);
if (!_nis_state.stayopen)
__grend_nis(&_nis_state);
if (rv == NS_SUCCESS)
*retval = &_nis_group;
return rv;
}
static int
_nis_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_nis state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
if (_nis_state.stayopen)
{
rv = __grscan_nis(retval, grp, buffer, buflen, &_nis_state, 1, name, 0);
}
else
{
memset(&state, 0, sizeof(state));
rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_nis(&state);
}
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif
#ifdef _GROUP_COMPAT
int
__grstart_compat(struct __grstate_compat *state)
{
_DIAGASSERT(state != NULL);
if (state->fp == NULL) {
state->fp = fopen(_PATH_GROUP, "re");
if (state->fp == NULL)
return NS_UNAVAIL;
} else {
rewind(state->fp);
}
return NS_SUCCESS;
}
int
__grend_compat(struct __grstate_compat *state)
{
_DIAGASSERT(state != NULL);
if (state->name) {
free(state->name);
state->name = NULL;
}
if (state->fp) {
(void) fclose(state->fp);
state->fp = NULL;
}
return NS_SUCCESS;
}
int
__grbad_compat(void *nsrv, void *nscb, va_list ap)
{
static int warned;
_DIAGASSERT(nsrv != NULL);
_DIAGASSERT(nscb != NULL);
if (!warned) {
syslog(LOG_ERR,
"nsswitch.conf group_compat database can't use '%s'",
(const char *)nscb);
}
warned = 1;
return NS_UNAVAIL;
}
int
__grscan_compat(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_compat *state, int search, const char *name, gid_t gid,
int (*searchfunc)(void *, struct group **), void *searchcookie)
{
int rv;
char filebuf[_GETGR_R_SIZE_MAX], *ep;
static const ns_dtab compatentdtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrent_r, NULL)
NS_NIS_CB(_nis_getgrent_r, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
static const ns_dtab compatgiddtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrgid_r, NULL)
NS_NIS_CB(_nis_getgrgid_r, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
static const ns_dtab compatnamdtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrnam_r, NULL)
NS_NIS_CB(_nis_getgrnam_r, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
*retval = 0;
if (state->fp == NULL) {
rv = __grstart_compat(state);
if (rv != NS_SUCCESS)
goto compatgrscan_out;
}
rv = NS_NOTFOUND;
for (;;) {
if (state->name != NULL) {
int crv, cretval;
struct group cgrp, *cgrpres;
if (state->name[0]) {
crv = nsdispatch(NULL, compatnamdtab,
NSDB_GROUP_COMPAT, "getgrnam_r",
__nsdefaultnis,
&cretval, state->name,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
free(state->name);
state->name = NULL;
} else if (!search) {
if (searchfunc) {
crv = searchfunc(searchcookie,
&cgrpres);
} else {
crv = nsdispatch(NULL, compatentdtab,
NSDB_GROUP_COMPAT, "getgrent_r",
__nsdefaultnis,
&cretval, &cgrp, filebuf,
sizeof(filebuf), &cgrpres);
}
} else if (name) {
crv = nsdispatch(NULL, compatnamdtab,
NSDB_GROUP_COMPAT, "getgrnam_r",
__nsdefaultnis,
&cretval, name,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
} else {
crv = nsdispatch(NULL, compatgiddtab,
NSDB_GROUP_COMPAT, "getgrgid_r",
__nsdefaultnis,
&cretval, gid,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
}
if (crv != NS_SUCCESS) {
free(state->name);
state->name = NULL;
continue;
}
if (!_gr_copy(cgrpres, grp, buffer, buflen)) {
rv = NS_UNAVAIL;
break;
}
goto compatgrscan_cmpgrp;
}
if (fgets(filebuf, (int)sizeof(filebuf), state->fp) == NULL)
break;
ep = strchr(filebuf, '\n');
if (ep == NULL) {
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
continue;
}
*ep = '\0';
if (filebuf[0] == '+') {
if (state->name)
free(state->name);
state->name = NULL;
switch(filebuf[1]) {
case ':':
case '\0':
state->name = strdup("");
break;
default:
ep = strchr(filebuf + 1, ':');
if (ep == NULL)
break;
*ep = '\0';
state->name = strdup(filebuf + 1);
break;
}
if (state->name == NULL) {
rv = NS_UNAVAIL;
break;
}
continue;
}
if (! _gr_parse(filebuf, grp, buffer, buflen)) {
continue;
}
compatgrscan_cmpgrp:
if (! search) {
rv = NS_SUCCESS;
break;
}
if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
break;
}
}
compatgrscan_out:
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
return rv;
}
static struct __grstate_compat _compat_state;
static struct group _compat_group;
static char _compat_groupbuf[_GETGR_R_SIZE_MAX];
static int
_compat_setgrent(void *nsrv, void *nscb, va_list ap)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_setgrent, NULL)
NS_NIS_CB(_nis_setgrent, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
__nsdefaultnis_forceall);
_compat_state.stayopen = 0;
return __grstart_compat(&_compat_state);
}
static int
_compat_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
static const ns_dtab dtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_setgroupent, NULL)
NS_NIS_CB(_nis_setgroupent, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgroupent",
__nsdefaultnis_forceall, &rv, stayopen);
_compat_state.stayopen = stayopen;
rv = __grstart_compat(&_compat_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
static int
_compat_endgrent(void *nsrv, void *nscb, va_list ap)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_endgrent, NULL)
NS_NIS_CB(_nis_endgrent, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
NS_NULL_CB
};
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
__nsdefaultnis_forceall);
_compat_state.stayopen = 0;
return __grend_compat(&_compat_state);
}
static int
_compat_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 0, NULL, 0, NULL, NULL);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
static int
_compat_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_compat(retval, grp, buffer, buflen,
&_compat_state, 0, NULL, 0, NULL, NULL);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
return rv;
}
static int
_compat_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_compat(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, NULL, gid, NULL, NULL);
if (!_compat_state.stayopen)
__grend_compat(&_compat_state);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
static int
_compat_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_compat state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1, NULL, gid, NULL, NULL);
__grend_compat(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
static int
_compat_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = __grstart_compat(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, name, 0, NULL, NULL);
if (!_compat_state.stayopen)
__grend_compat(&_compat_state);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
static int
_compat_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct __grstate_compat state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1, name, 0, NULL, NULL);
__grend_compat(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif
struct group *
getgrent(void)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrent, NULL)
NS_DNS_CB(_dns_getgrent, NULL)
NS_NIS_CB(_nis_getgrent, NULL)
NS_COMPAT_CB(_compat_getgrent, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent", __nsdefaultcompat,
&retval);
mutex_unlock(&__grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
int
getgrent_r(struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrent_r, NULL)
NS_DNS_CB(_dns_getgrent_r, NULL)
NS_NIS_CB(_nis_getgrent_r, NULL)
NS_COMPAT_CB(_compat_getgrent_r, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent_r", __nsdefaultcompat,
&retval, grp, buffer, buflen, result);
mutex_unlock(&__grmutex);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
struct group *
getgrgid(gid_t gid)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrgid, NULL)
NS_DNS_CB(_dns_getgrgid, NULL)
NS_NIS_CB(_nis_getgrgid, NULL)
NS_COMPAT_CB(_compat_getgrgid, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid", __nsdefaultcompat,
&retval, gid);
mutex_unlock(&__grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
int
getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrgid_r, NULL)
NS_DNS_CB(_dns_getgrgid_r, NULL)
NS_NIS_CB(_nis_getgrgid_r, NULL)
NS_COMPAT_CB(_compat_getgrgid_r, NULL)
NS_NULL_CB
};
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
retval = 0;
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid_r", __nsdefaultcompat,
&retval, gid, grp, buffer, buflen, result);
mutex_unlock(&__grmutex);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
struct group *
getgrnam(const char *name)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrnam, NULL)
NS_DNS_CB(_dns_getgrnam, NULL)
NS_NIS_CB(_nis_getgrnam, NULL)
NS_COMPAT_CB(_compat_getgrnam, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam", __nsdefaultcompat,
&retval, name);
mutex_unlock(&__grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
int
getgrnam_r(const char *name, struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrnam_r, NULL)
NS_DNS_CB(_dns_getgrnam_r, NULL)
NS_NIS_CB(_nis_getgrnam_r, NULL)
NS_COMPAT_CB(_compat_getgrnam_r, NULL)
NS_NULL_CB
};
_DIAGASSERT(name != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
retval = 0;
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam_r", __nsdefaultcompat,
&retval, name, grp, buffer, buflen, result);
mutex_unlock(&__grmutex);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
void
endgrent(void)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_endgrent, NULL)
NS_DNS_CB(_dns_endgrent, NULL)
NS_NIS_CB(_nis_endgrent, NULL)
NS_COMPAT_CB(_compat_endgrent, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
(void) nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent",
__nsdefaultcompat_forceall);
mutex_unlock(&__grmutex);
}
int
setgroupent(int stayopen)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_setgroupent, NULL)
NS_DNS_CB(_dns_setgroupent, NULL)
NS_NIS_CB(_nis_setgroupent, NULL)
NS_COMPAT_CB(_compat_setgroupent, NULL)
NS_NULL_CB
};
int rv, retval;
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "setgroupent",
__nsdefaultcompat_forceall, &retval, stayopen);
mutex_unlock(&__grmutex);
return (rv == NS_SUCCESS) ? retval : 0;
}
void
setgrent(void)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_setgrent, NULL)
NS_DNS_CB(_dns_setgrent, NULL)
NS_NIS_CB(_nis_setgrent, NULL)
NS_COMPAT_CB(_compat_setgrent, NULL)
NS_NULL_CB
};
mutex_lock(&__grmutex);
(void) nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent",
__nsdefaultcompat_forceall);
mutex_unlock(&__grmutex);
}