9c24291370
Just like with freelocale(3), I haven't been able to find any piece of code that actually makes use of this function's return value, both in base and in ports. The reason for this is that FreeBSD seems to be the only operating system to have such a prototype. This is why I'm deciding to not use symbol versioning for this. It does seem that the pw(8) utility depends on the function's typing and already had a switch in place to toggle between the FreeBSD and POSIX variant of this function. Clean this up by always expecting the POSIX variant. There is also a single port that has a couple of local declarations of setgrent(3) that need to be patched up. This is in the process of being fixed. PR: 211394 (exp-run)
1553 lines
33 KiB
C
1553 lines
33 KiB
C
/*-
|
|
* Copyright (c) 2003 Networks Associates Technology, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed for the FreeBSD Project by
|
|
* Jacques A. Vidrine, Safeport Network Services, and Network
|
|
* Associates Laboratories, the Security Research Division of Network
|
|
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
|
|
* ("CBOSS"), as part of the DARPA CHATS research program.
|
|
*
|
|
* 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 AUTHOR 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 AUTHOR 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>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "namespace.h"
|
|
#include <sys/param.h>
|
|
#ifdef YP
|
|
#include <rpc/rpc.h>
|
|
#include <rpcsvc/yp_prot.h>
|
|
#include <rpcsvc/ypclnt.h>
|
|
#endif
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#ifdef HESIOD
|
|
#include <hesiod.h>
|
|
#endif
|
|
#include <grp.h>
|
|
#include <nsswitch.h>
|
|
#include <pthread.h>
|
|
#include <pthread_np.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
#include "un-namespace.h"
|
|
#include "libc_private.h"
|
|
#include "nss_tls.h"
|
|
#ifdef NS_CACHING
|
|
#include "nscache.h"
|
|
#endif
|
|
|
|
enum constants {
|
|
GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
|
|
GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */
|
|
SETGRENT = 1,
|
|
ENDGRENT = 2,
|
|
HESIOD_NAME_MAX = 256,
|
|
};
|
|
|
|
static const ns_src defaultsrc[] = {
|
|
{ NSSRC_COMPAT, NS_SUCCESS },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
int __getgroupmembership(const char *, gid_t, gid_t *, int, int *);
|
|
int __gr_match_entry(const char *, size_t, enum nss_lookup_type,
|
|
const char *, gid_t);
|
|
int __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
|
|
int *);
|
|
|
|
static int is_comment_line(const char *, size_t);
|
|
|
|
union key {
|
|
const char *name;
|
|
gid_t gid;
|
|
};
|
|
static struct group *getgr(int (*)(union key, struct group *, char *, size_t,
|
|
struct group **), union key);
|
|
static int wrap_getgrnam_r(union key, struct group *, char *, size_t,
|
|
struct group **);
|
|
static int wrap_getgrgid_r(union key, struct group *, char *, size_t,
|
|
struct group **);
|
|
static int wrap_getgrent_r(union key, struct group *, char *, size_t,
|
|
struct group **);
|
|
|
|
struct files_state {
|
|
FILE *fp;
|
|
int stayopen;
|
|
};
|
|
static void files_endstate(void *);
|
|
NSS_TLS_HANDLING(files);
|
|
static int files_setgrent(void *, void *, va_list);
|
|
static int files_group(void *, void *, va_list);
|
|
|
|
|
|
#ifdef HESIOD
|
|
struct dns_state {
|
|
long counter;
|
|
};
|
|
static void dns_endstate(void *);
|
|
NSS_TLS_HANDLING(dns);
|
|
static int dns_setgrent(void *, void *, va_list);
|
|
static int dns_group(void *, void *, va_list);
|
|
#endif
|
|
|
|
|
|
#ifdef YP
|
|
struct nis_state {
|
|
char domain[MAXHOSTNAMELEN];
|
|
int done;
|
|
char *key;
|
|
int keylen;
|
|
};
|
|
static void nis_endstate(void *);
|
|
NSS_TLS_HANDLING(nis);
|
|
static int nis_setgrent(void *, void *, va_list);
|
|
static int nis_group(void *, void *, va_list);
|
|
#endif
|
|
|
|
struct compat_state {
|
|
FILE *fp;
|
|
int stayopen;
|
|
char *name;
|
|
enum _compat {
|
|
COMPAT_MODE_OFF = 0,
|
|
COMPAT_MODE_ALL,
|
|
COMPAT_MODE_NAME
|
|
} compat;
|
|
};
|
|
static void compat_endstate(void *);
|
|
NSS_TLS_HANDLING(compat);
|
|
static int compat_setgrent(void *, void *, va_list);
|
|
static int compat_group(void *, void *, va_list);
|
|
|
|
static int gr_addgid(gid_t, gid_t *, int, int *);
|
|
static int getgroupmembership_fallback(void *, void *, va_list);
|
|
|
|
#ifdef NS_CACHING
|
|
static int grp_id_func(char *, size_t *, va_list, void *);
|
|
static int grp_marshal_func(char *, size_t *, void *, va_list, void *);
|
|
static int grp_unmarshal_func(char *, size_t, void *, va_list, void *);
|
|
|
|
static int
|
|
grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
|
|
{
|
|
char *name;
|
|
gid_t gid;
|
|
|
|
size_t desired_size, size;
|
|
int res = NS_UNAVAIL;
|
|
enum nss_lookup_type lookup_type;
|
|
|
|
|
|
lookup_type = (enum nss_lookup_type)cache_mdata;
|
|
switch (lookup_type) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, char *);
|
|
size = strlen(name);
|
|
desired_size = sizeof(enum nss_lookup_type) + size + 1;
|
|
if (desired_size > *buffer_size) {
|
|
res = NS_RETURN;
|
|
goto fin;
|
|
}
|
|
|
|
memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
|
|
memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
|
|
|
|
res = NS_SUCCESS;
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t);
|
|
if (desired_size > *buffer_size) {
|
|
res = NS_RETURN;
|
|
goto fin;
|
|
}
|
|
|
|
memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
|
|
memcpy(buffer + sizeof(enum nss_lookup_type), &gid,
|
|
sizeof(gid_t));
|
|
|
|
res = NS_SUCCESS;
|
|
break;
|
|
default:
|
|
/* should be unreachable */
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
fin:
|
|
*buffer_size = desired_size;
|
|
return (res);
|
|
}
|
|
|
|
static int
|
|
grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
|
|
void *cache_mdata)
|
|
{
|
|
char *name;
|
|
gid_t gid;
|
|
struct group *grp;
|
|
char *orig_buf;
|
|
size_t orig_buf_size;
|
|
|
|
struct group new_grp;
|
|
size_t desired_size, size, mem_size;
|
|
char *p, **mem;
|
|
|
|
switch ((enum nss_lookup_type)cache_mdata) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, char *);
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
break;
|
|
case nss_lt_all:
|
|
break;
|
|
default:
|
|
/* should be unreachable */
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
grp = va_arg(ap, struct group *);
|
|
orig_buf = va_arg(ap, char *);
|
|
orig_buf_size = va_arg(ap, size_t);
|
|
|
|
desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *);
|
|
|
|
if (grp->gr_name != NULL)
|
|
desired_size += strlen(grp->gr_name) + 1;
|
|
if (grp->gr_passwd != NULL)
|
|
desired_size += strlen(grp->gr_passwd) + 1;
|
|
|
|
if (grp->gr_mem != NULL) {
|
|
mem_size = 0;
|
|
for (mem = grp->gr_mem; *mem; ++mem) {
|
|
desired_size += strlen(*mem) + 1;
|
|
++mem_size;
|
|
}
|
|
|
|
desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *);
|
|
}
|
|
|
|
if (desired_size > *buffer_size) {
|
|
/* this assignment is here for future use */
|
|
*buffer_size = desired_size;
|
|
return (NS_RETURN);
|
|
}
|
|
|
|
memcpy(&new_grp, grp, sizeof(struct group));
|
|
memset(buffer, 0, desired_size);
|
|
|
|
*buffer_size = desired_size;
|
|
p = buffer + sizeof(struct group) + sizeof(char *);
|
|
memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
|
|
p = (char *)_ALIGN(p);
|
|
|
|
if (new_grp.gr_name != NULL) {
|
|
size = strlen(new_grp.gr_name);
|
|
memcpy(p, new_grp.gr_name, size);
|
|
new_grp.gr_name = p;
|
|
p += size + 1;
|
|
}
|
|
|
|
if (new_grp.gr_passwd != NULL) {
|
|
size = strlen(new_grp.gr_passwd);
|
|
memcpy(p, new_grp.gr_passwd, size);
|
|
new_grp.gr_passwd = p;
|
|
p += size + 1;
|
|
}
|
|
|
|
if (new_grp.gr_mem != NULL) {
|
|
p = (char *)_ALIGN(p);
|
|
memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
|
|
new_grp.gr_mem = (char **)p;
|
|
p += sizeof(char *) * (mem_size + 1);
|
|
|
|
for (mem = new_grp.gr_mem; *mem; ++mem) {
|
|
size = strlen(*mem);
|
|
memcpy(p, *mem, size);
|
|
*mem = p;
|
|
p += size + 1;
|
|
}
|
|
}
|
|
|
|
memcpy(buffer, &new_grp, sizeof(struct group));
|
|
return (NS_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
|
|
void *cache_mdata)
|
|
{
|
|
char *name;
|
|
gid_t gid;
|
|
struct group *grp;
|
|
char *orig_buf;
|
|
size_t orig_buf_size;
|
|
int *ret_errno;
|
|
|
|
char *p;
|
|
char **mem;
|
|
|
|
switch ((enum nss_lookup_type)cache_mdata) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, char *);
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
break;
|
|
case nss_lt_all:
|
|
break;
|
|
default:
|
|
/* should be unreachable */
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
grp = va_arg(ap, struct group *);
|
|
orig_buf = va_arg(ap, char *);
|
|
orig_buf_size = va_arg(ap, size_t);
|
|
ret_errno = va_arg(ap, int *);
|
|
|
|
if (orig_buf_size <
|
|
buffer_size - sizeof(struct group) - sizeof(char *)) {
|
|
*ret_errno = ERANGE;
|
|
return (NS_RETURN);
|
|
}
|
|
|
|
memcpy(grp, buffer, sizeof(struct group));
|
|
memcpy(&p, buffer + sizeof(struct group), sizeof(char *));
|
|
|
|
orig_buf = (char *)_ALIGN(orig_buf);
|
|
memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) +
|
|
_ALIGN(p) - (size_t)p,
|
|
buffer_size - sizeof(struct group) - sizeof(char *) -
|
|
_ALIGN(p) + (size_t)p);
|
|
p = (char *)_ALIGN(p);
|
|
|
|
NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *);
|
|
NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *);
|
|
if (grp->gr_mem != NULL) {
|
|
NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **);
|
|
|
|
for (mem = grp->gr_mem; *mem; ++mem)
|
|
NS_APPLY_OFFSET(*mem, orig_buf, p, char *);
|
|
}
|
|
|
|
if (retval != NULL)
|
|
*((struct group **)retval) = grp;
|
|
|
|
return (NS_SUCCESS);
|
|
}
|
|
|
|
NSS_MP_CACHE_HANDLING(group);
|
|
#endif /* NS_CACHING */
|
|
|
|
#ifdef NS_CACHING
|
|
static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
|
|
group, (void *)nss_lt_all,
|
|
NULL, NULL);
|
|
#endif
|
|
|
|
static const ns_dtab setgrent_dtab[] = {
|
|
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
|
|
#endif
|
|
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
|
|
#ifdef NS_CACHING
|
|
NS_CACHE_CB(&setgrent_cache_info)
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
#ifdef NS_CACHING
|
|
static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
|
|
group, (void *)nss_lt_all,
|
|
NULL, NULL);
|
|
#endif
|
|
|
|
static const ns_dtab endgrent_dtab[] = {
|
|
{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
|
|
#endif
|
|
{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
|
|
#ifdef NS_CACHING
|
|
NS_CACHE_CB(&endgrent_cache_info)
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
#ifdef NS_CACHING
|
|
static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
|
|
group, (void *)nss_lt_all,
|
|
grp_marshal_func, grp_unmarshal_func);
|
|
#endif
|
|
|
|
static const ns_dtab getgrent_r_dtab[] = {
|
|
{ NSSRC_FILES, files_group, (void *)nss_lt_all },
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_group, (void *)nss_lt_all },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
|
|
#endif
|
|
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
|
|
#ifdef NS_CACHING
|
|
NS_CACHE_CB(&getgrent_r_cache_info)
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
static int
|
|
gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt)
|
|
{
|
|
int ret, dupc;
|
|
|
|
for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) {
|
|
if (groups[dupc] == gid)
|
|
return 1;
|
|
}
|
|
|
|
ret = 1;
|
|
if (*grpcnt < maxgrp)
|
|
groups[*grpcnt] = gid;
|
|
else
|
|
ret = 0;
|
|
|
|
(*grpcnt)++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
getgroupmembership_fallback(void *retval, void *mdata, va_list ap)
|
|
{
|
|
const ns_src src[] = {
|
|
{ mdata, NS_SUCCESS },
|
|
{ NULL, 0}
|
|
};
|
|
struct group grp;
|
|
struct group *grp_p;
|
|
char *buf;
|
|
size_t bufsize;
|
|
const char *uname;
|
|
gid_t *groups;
|
|
gid_t agroup;
|
|
int maxgrp, *grpcnt;
|
|
int i, rv, ret_errno;
|
|
|
|
/*
|
|
* As this is a fallback method, only provided src
|
|
* list will be respected during methods search.
|
|
*/
|
|
assert(src[0].name != NULL);
|
|
|
|
uname = va_arg(ap, const char *);
|
|
agroup = va_arg(ap, gid_t);
|
|
groups = va_arg(ap, gid_t *);
|
|
maxgrp = va_arg(ap, int);
|
|
grpcnt = va_arg(ap, int *);
|
|
|
|
rv = NS_UNAVAIL;
|
|
|
|
buf = malloc(GRP_STORAGE_INITIAL);
|
|
if (buf == NULL)
|
|
goto out;
|
|
|
|
bufsize = GRP_STORAGE_INITIAL;
|
|
|
|
gr_addgid(agroup, groups, maxgrp, grpcnt);
|
|
|
|
_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0);
|
|
for (;;) {
|
|
do {
|
|
ret_errno = 0;
|
|
grp_p = NULL;
|
|
rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP,
|
|
"getgrent_r", src, &grp, buf, bufsize, &ret_errno);
|
|
|
|
if (grp_p == NULL && ret_errno == ERANGE) {
|
|
free(buf);
|
|
if ((bufsize << 1) > GRP_STORAGE_MAX) {
|
|
buf = NULL;
|
|
errno = ERANGE;
|
|
goto out;
|
|
}
|
|
|
|
bufsize <<= 1;
|
|
buf = malloc(bufsize);
|
|
if (buf == NULL) {
|
|
goto out;
|
|
}
|
|
}
|
|
} while (grp_p == NULL && ret_errno == ERANGE);
|
|
|
|
if (ret_errno != 0) {
|
|
errno = ret_errno;
|
|
goto out;
|
|
}
|
|
|
|
if (grp_p == NULL)
|
|
break;
|
|
|
|
for (i = 0; grp.gr_mem[i]; i++) {
|
|
if (strcmp(grp.gr_mem[i], uname) == 0)
|
|
gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt);
|
|
}
|
|
}
|
|
|
|
_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src);
|
|
out:
|
|
free(buf);
|
|
return (rv);
|
|
}
|
|
|
|
void
|
|
setgrent(void)
|
|
{
|
|
(void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
|
|
}
|
|
|
|
|
|
int
|
|
setgroupent(int stayopen)
|
|
{
|
|
(void)_nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc,
|
|
stayopen);
|
|
return (1);
|
|
}
|
|
|
|
|
|
void
|
|
endgrent(void)
|
|
{
|
|
(void)_nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc);
|
|
}
|
|
|
|
|
|
int
|
|
getgrent_r(struct group *grp, char *buffer, size_t bufsize,
|
|
struct group **result)
|
|
{
|
|
int rv, ret_errno;
|
|
|
|
ret_errno = 0;
|
|
*result = NULL;
|
|
rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
|
|
grp, buffer, bufsize, &ret_errno);
|
|
if (rv == NS_SUCCESS)
|
|
return (0);
|
|
else
|
|
return (ret_errno);
|
|
}
|
|
|
|
|
|
int
|
|
getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
|
|
struct group **result)
|
|
{
|
|
#ifdef NS_CACHING
|
|
static const nss_cache_info cache_info =
|
|
NS_COMMON_CACHE_INFO_INITIALIZER(
|
|
group, (void *)nss_lt_name,
|
|
grp_id_func, grp_marshal_func, grp_unmarshal_func);
|
|
#endif
|
|
|
|
static const ns_dtab dtab[] = {
|
|
{ NSSRC_FILES, files_group, (void *)nss_lt_name },
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_group, (void *)nss_lt_name },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_group, (void *)nss_lt_name },
|
|
#endif
|
|
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
|
|
#ifdef NS_CACHING
|
|
NS_CACHE_CB(&cache_info)
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
int rv, ret_errno;
|
|
|
|
ret_errno = 0;
|
|
*result = NULL;
|
|
rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc,
|
|
name, grp, buffer, bufsize, &ret_errno);
|
|
if (rv == NS_SUCCESS)
|
|
return (0);
|
|
else
|
|
return (ret_errno);
|
|
}
|
|
|
|
|
|
int
|
|
getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
|
|
struct group **result)
|
|
{
|
|
#ifdef NS_CACHING
|
|
static const nss_cache_info cache_info =
|
|
NS_COMMON_CACHE_INFO_INITIALIZER(
|
|
group, (void *)nss_lt_id,
|
|
grp_id_func, grp_marshal_func, grp_unmarshal_func);
|
|
#endif
|
|
|
|
static const ns_dtab dtab[] = {
|
|
{ NSSRC_FILES, files_group, (void *)nss_lt_id },
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_group, (void *)nss_lt_id },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_group, (void *)nss_lt_id },
|
|
#endif
|
|
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
|
|
#ifdef NS_CACHING
|
|
NS_CACHE_CB(&cache_info)
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
int rv, ret_errno;
|
|
|
|
ret_errno = 0;
|
|
*result = NULL;
|
|
rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc,
|
|
gid, grp, buffer, bufsize, &ret_errno);
|
|
if (rv == NS_SUCCESS)
|
|
return (0);
|
|
else
|
|
return (ret_errno);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
__getgroupmembership(const char *uname, gid_t agroup, gid_t *groups,
|
|
int maxgrp, int *grpcnt)
|
|
{
|
|
static const ns_dtab dtab[] = {
|
|
NS_FALLBACK_CB(getgroupmembership_fallback)
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
assert(uname != NULL);
|
|
/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
|
|
assert(grpcnt != NULL);
|
|
|
|
*grpcnt = 0;
|
|
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
|
|
defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
|
|
|
|
/* too many groups found? */
|
|
return (*grpcnt > maxgrp ? -1 : 0);
|
|
}
|
|
|
|
|
|
static struct group grp;
|
|
static char *grp_storage;
|
|
static size_t grp_storage_size;
|
|
|
|
static struct group *
|
|
getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
|
|
union key key)
|
|
{
|
|
int rv;
|
|
struct group *res;
|
|
|
|
if (grp_storage == NULL) {
|
|
grp_storage = malloc(GRP_STORAGE_INITIAL);
|
|
if (grp_storage == NULL)
|
|
return (NULL);
|
|
grp_storage_size = GRP_STORAGE_INITIAL;
|
|
}
|
|
do {
|
|
rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
|
|
if (res == NULL && rv == ERANGE) {
|
|
free(grp_storage);
|
|
if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
|
|
grp_storage = NULL;
|
|
errno = ERANGE;
|
|
return (NULL);
|
|
}
|
|
grp_storage_size <<= 1;
|
|
grp_storage = malloc(grp_storage_size);
|
|
if (grp_storage == NULL)
|
|
return (NULL);
|
|
}
|
|
} while (res == NULL && rv == ERANGE);
|
|
if (rv != 0)
|
|
errno = rv;
|
|
return (res);
|
|
}
|
|
|
|
|
|
static int
|
|
wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
|
|
struct group **res)
|
|
{
|
|
return (getgrnam_r(key.name, grp, buffer, bufsize, res));
|
|
}
|
|
|
|
|
|
static int
|
|
wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
|
|
struct group **res)
|
|
{
|
|
return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
|
|
}
|
|
|
|
|
|
static int
|
|
wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
|
|
size_t bufsize, struct group **res)
|
|
{
|
|
return (getgrent_r(grp, buffer, bufsize, res));
|
|
}
|
|
|
|
|
|
struct group *
|
|
getgrnam(const char *name)
|
|
{
|
|
union key key;
|
|
|
|
key.name = name;
|
|
return (getgr(wrap_getgrnam_r, key));
|
|
}
|
|
|
|
|
|
struct group *
|
|
getgrgid(gid_t gid)
|
|
{
|
|
union key key;
|
|
|
|
key.gid = gid;
|
|
return (getgr(wrap_getgrgid_r, key));
|
|
}
|
|
|
|
|
|
struct group *
|
|
getgrent(void)
|
|
{
|
|
union key key;
|
|
|
|
key.gid = 0; /* not used */
|
|
return (getgr(wrap_getgrent_r, key));
|
|
}
|
|
|
|
|
|
static int
|
|
is_comment_line(const char *s, size_t n)
|
|
{
|
|
const char *eom;
|
|
|
|
eom = &s[n];
|
|
|
|
for (; s < eom; s++)
|
|
if (*s == '#' || !isspace((unsigned char)*s))
|
|
break;
|
|
return (*s == '#' || s == eom);
|
|
}
|
|
|
|
|
|
/*
|
|
* files backend
|
|
*/
|
|
static void
|
|
files_endstate(void *p)
|
|
{
|
|
|
|
if (p == NULL)
|
|
return;
|
|
if (((struct files_state *)p)->fp != NULL)
|
|
fclose(((struct files_state *)p)->fp);
|
|
free(p);
|
|
}
|
|
|
|
|
|
static int
|
|
files_setgrent(void *retval, void *mdata, va_list ap)
|
|
{
|
|
struct files_state *st;
|
|
int rv, stayopen;
|
|
|
|
rv = files_getstate(&st);
|
|
if (rv != 0)
|
|
return (NS_UNAVAIL);
|
|
switch ((enum constants)mdata) {
|
|
case SETGRENT:
|
|
stayopen = va_arg(ap, int);
|
|
if (st->fp != NULL)
|
|
rewind(st->fp);
|
|
else if (stayopen)
|
|
st->fp = fopen(_PATH_GROUP, "re");
|
|
break;
|
|
case ENDGRENT:
|
|
if (st->fp != NULL) {
|
|
fclose(st->fp);
|
|
st->fp = NULL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
|
|
static int
|
|
files_group(void *retval, void *mdata, va_list ap)
|
|
{
|
|
struct files_state *st;
|
|
enum nss_lookup_type how;
|
|
const char *name, *line;
|
|
struct group *grp;
|
|
gid_t gid;
|
|
char *buffer;
|
|
size_t bufsize, linesize;
|
|
off_t pos;
|
|
int rv, stayopen, *errnop;
|
|
|
|
name = NULL;
|
|
gid = (gid_t)-1;
|
|
how = (enum nss_lookup_type)mdata;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, const char *);
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
break;
|
|
case nss_lt_all:
|
|
break;
|
|
default:
|
|
return (NS_NOTFOUND);
|
|
}
|
|
grp = va_arg(ap, struct group *);
|
|
buffer = va_arg(ap, char *);
|
|
bufsize = va_arg(ap, size_t);
|
|
errnop = va_arg(ap, int *);
|
|
*errnop = files_getstate(&st);
|
|
if (*errnop != 0)
|
|
return (NS_UNAVAIL);
|
|
if (st->fp == NULL &&
|
|
((st->fp = fopen(_PATH_GROUP, "re")) == NULL)) {
|
|
*errnop = errno;
|
|
return (NS_UNAVAIL);
|
|
}
|
|
if (how == nss_lt_all)
|
|
stayopen = 1;
|
|
else {
|
|
rewind(st->fp);
|
|
stayopen = st->stayopen;
|
|
}
|
|
rv = NS_NOTFOUND;
|
|
pos = ftello(st->fp);
|
|
while ((line = fgetln(st->fp, &linesize)) != NULL) {
|
|
if (line[linesize-1] == '\n')
|
|
linesize--;
|
|
rv = __gr_match_entry(line, linesize, how, name, gid);
|
|
if (rv != NS_SUCCESS)
|
|
continue;
|
|
/* We need room at least for the line, a string NUL
|
|
* terminator, alignment padding, and one (char *)
|
|
* pointer for the member list terminator.
|
|
*/
|
|
if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
|
|
*errnop = ERANGE;
|
|
rv = NS_RETURN;
|
|
break;
|
|
}
|
|
memcpy(buffer, line, linesize);
|
|
buffer[linesize] = '\0';
|
|
rv = __gr_parse_entry(buffer, linesize, grp,
|
|
&buffer[linesize + 1], bufsize - linesize - 1, errnop);
|
|
if (rv & NS_TERMINATE)
|
|
break;
|
|
pos = ftello(st->fp);
|
|
}
|
|
if (st->fp != NULL && !stayopen) {
|
|
fclose(st->fp);
|
|
st->fp = NULL;
|
|
}
|
|
if (rv == NS_SUCCESS && retval != NULL)
|
|
*(struct group **)retval = grp;
|
|
else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
|
|
fseeko(st->fp, pos, SEEK_SET);
|
|
return (rv);
|
|
}
|
|
|
|
|
|
#ifdef HESIOD
|
|
/*
|
|
* dns backend
|
|
*/
|
|
static void
|
|
dns_endstate(void *p)
|
|
{
|
|
|
|
free(p);
|
|
}
|
|
|
|
|
|
static int
|
|
dns_setgrent(void *retval, void *cb_data, va_list ap)
|
|
{
|
|
struct dns_state *st;
|
|
int rv;
|
|
|
|
rv = dns_getstate(&st);
|
|
if (rv != 0)
|
|
return (NS_UNAVAIL);
|
|
st->counter = 0;
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
|
|
static int
|
|
dns_group(void *retval, void *mdata, va_list ap)
|
|
{
|
|
char buf[HESIOD_NAME_MAX];
|
|
struct dns_state *st;
|
|
struct group *grp;
|
|
const char *name, *label;
|
|
void *ctx;
|
|
char *buffer, **hes;
|
|
size_t bufsize, adjsize, linesize;
|
|
gid_t gid;
|
|
enum nss_lookup_type how;
|
|
int rv, *errnop;
|
|
|
|
ctx = NULL;
|
|
hes = NULL;
|
|
name = NULL;
|
|
gid = (gid_t)-1;
|
|
how = (enum nss_lookup_type)mdata;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, const char *);
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
break;
|
|
case nss_lt_all:
|
|
break;
|
|
}
|
|
grp = va_arg(ap, struct group *);
|
|
buffer = va_arg(ap, char *);
|
|
bufsize = va_arg(ap, size_t);
|
|
errnop = va_arg(ap, int *);
|
|
*errnop = dns_getstate(&st);
|
|
if (*errnop != 0)
|
|
return (NS_UNAVAIL);
|
|
if (hesiod_init(&ctx) != 0) {
|
|
*errnop = errno;
|
|
rv = NS_UNAVAIL;
|
|
goto fin;
|
|
}
|
|
do {
|
|
rv = NS_NOTFOUND;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
label = name;
|
|
break;
|
|
case nss_lt_id:
|
|
if (snprintf(buf, sizeof(buf), "%lu",
|
|
(unsigned long)gid) >= sizeof(buf))
|
|
goto fin;
|
|
label = buf;
|
|
break;
|
|
case nss_lt_all:
|
|
if (st->counter < 0)
|
|
goto fin;
|
|
if (snprintf(buf, sizeof(buf), "group-%ld",
|
|
st->counter++) >= sizeof(buf))
|
|
goto fin;
|
|
label = buf;
|
|
break;
|
|
}
|
|
hes = hesiod_resolve(ctx, label,
|
|
how == nss_lt_id ? "gid" : "group");
|
|
if ((how == nss_lt_id && hes == NULL &&
|
|
(hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
|
|
hes == NULL) {
|
|
if (how == nss_lt_all)
|
|
st->counter = -1;
|
|
if (errno != ENOENT)
|
|
*errnop = errno;
|
|
goto fin;
|
|
}
|
|
rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
|
|
if (rv != NS_SUCCESS) {
|
|
hesiod_free_list(ctx, hes);
|
|
hes = NULL;
|
|
continue;
|
|
}
|
|
/* We need room at least for the line, a string NUL
|
|
* terminator, alignment padding, and one (char *)
|
|
* pointer for the member list terminator.
|
|
*/
|
|
adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
|
|
linesize = strlcpy(buffer, hes[0], adjsize);
|
|
if (linesize >= adjsize) {
|
|
*errnop = ERANGE;
|
|
rv = NS_RETURN;
|
|
goto fin;
|
|
}
|
|
hesiod_free_list(ctx, hes);
|
|
hes = NULL;
|
|
rv = __gr_parse_entry(buffer, linesize, grp,
|
|
&buffer[linesize + 1], bufsize - linesize - 1, errnop);
|
|
} while (how == nss_lt_all && !(rv & NS_TERMINATE));
|
|
fin:
|
|
if (hes != NULL)
|
|
hesiod_free_list(ctx, hes);
|
|
if (ctx != NULL)
|
|
hesiod_end(ctx);
|
|
if (rv == NS_SUCCESS && retval != NULL)
|
|
*(struct group **)retval = grp;
|
|
return (rv);
|
|
}
|
|
#endif /* HESIOD */
|
|
|
|
|
|
#ifdef YP
|
|
/*
|
|
* nis backend
|
|
*/
|
|
static void
|
|
nis_endstate(void *p)
|
|
{
|
|
|
|
if (p == NULL)
|
|
return;
|
|
free(((struct nis_state *)p)->key);
|
|
free(p);
|
|
}
|
|
|
|
|
|
static int
|
|
nis_setgrent(void *retval, void *cb_data, va_list ap)
|
|
{
|
|
struct nis_state *st;
|
|
int rv;
|
|
|
|
rv = nis_getstate(&st);
|
|
if (rv != 0)
|
|
return (NS_UNAVAIL);
|
|
st->done = 0;
|
|
free(st->key);
|
|
st->key = NULL;
|
|
return (NS_UNAVAIL);
|
|
}
|
|
|
|
|
|
static int
|
|
nis_group(void *retval, void *mdata, va_list ap)
|
|
{
|
|
char *map;
|
|
struct nis_state *st;
|
|
struct group *grp;
|
|
const char *name;
|
|
char *buffer, *key, *result;
|
|
size_t bufsize;
|
|
gid_t gid;
|
|
enum nss_lookup_type how;
|
|
int *errnop, keylen, resultlen, rv;
|
|
|
|
name = NULL;
|
|
gid = (gid_t)-1;
|
|
how = (enum nss_lookup_type)mdata;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, const char *);
|
|
map = "group.byname";
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
map = "group.bygid";
|
|
break;
|
|
case nss_lt_all:
|
|
map = "group.byname";
|
|
break;
|
|
}
|
|
grp = va_arg(ap, struct group *);
|
|
buffer = va_arg(ap, char *);
|
|
bufsize = va_arg(ap, size_t);
|
|
errnop = va_arg(ap, int *);
|
|
*errnop = nis_getstate(&st);
|
|
if (*errnop != 0)
|
|
return (NS_UNAVAIL);
|
|
if (st->domain[0] == '\0') {
|
|
if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
|
|
*errnop = errno;
|
|
return (NS_UNAVAIL);
|
|
}
|
|
}
|
|
result = NULL;
|
|
do {
|
|
rv = NS_NOTFOUND;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
if (strlcpy(buffer, name, bufsize) >= bufsize)
|
|
goto erange;
|
|
break;
|
|
case nss_lt_id:
|
|
if (snprintf(buffer, bufsize, "%lu",
|
|
(unsigned long)gid) >= bufsize)
|
|
goto erange;
|
|
break;
|
|
case nss_lt_all:
|
|
if (st->done)
|
|
goto fin;
|
|
break;
|
|
}
|
|
result = NULL;
|
|
if (how == nss_lt_all) {
|
|
if (st->key == NULL)
|
|
rv = yp_first(st->domain, map, &st->key,
|
|
&st->keylen, &result, &resultlen);
|
|
else {
|
|
key = st->key;
|
|
keylen = st->keylen;
|
|
st->key = NULL;
|
|
rv = yp_next(st->domain, map, key, keylen,
|
|
&st->key, &st->keylen, &result,
|
|
&resultlen);
|
|
free(key);
|
|
}
|
|
if (rv != 0) {
|
|
free(result);
|
|
free(st->key);
|
|
st->key = NULL;
|
|
if (rv == YPERR_NOMORE) {
|
|
st->done = 1;
|
|
rv = NS_NOTFOUND;
|
|
} else
|
|
rv = NS_UNAVAIL;
|
|
goto fin;
|
|
}
|
|
} else {
|
|
rv = yp_match(st->domain, map, buffer, strlen(buffer),
|
|
&result, &resultlen);
|
|
if (rv == YPERR_KEY) {
|
|
rv = NS_NOTFOUND;
|
|
continue;
|
|
} else if (rv != 0) {
|
|
free(result);
|
|
rv = NS_UNAVAIL;
|
|
continue;
|
|
}
|
|
}
|
|
/* We need room at least for the line, a string NUL
|
|
* terminator, alignment padding, and one (char *)
|
|
* pointer for the member list terminator.
|
|
*/
|
|
if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *)) {
|
|
free(result);
|
|
goto erange;
|
|
}
|
|
memcpy(buffer, result, resultlen);
|
|
buffer[resultlen] = '\0';
|
|
free(result);
|
|
rv = __gr_match_entry(buffer, resultlen, how, name, gid);
|
|
if (rv == NS_SUCCESS)
|
|
rv = __gr_parse_entry(buffer, resultlen, grp,
|
|
&buffer[resultlen+1], bufsize - resultlen - 1,
|
|
errnop);
|
|
} while (how == nss_lt_all && !(rv & NS_TERMINATE));
|
|
fin:
|
|
if (rv == NS_SUCCESS && retval != NULL)
|
|
*(struct group **)retval = grp;
|
|
return (rv);
|
|
erange:
|
|
*errnop = ERANGE;
|
|
return (NS_RETURN);
|
|
}
|
|
#endif /* YP */
|
|
|
|
|
|
|
|
/*
|
|
* compat backend
|
|
*/
|
|
static void
|
|
compat_endstate(void *p)
|
|
{
|
|
struct compat_state *st;
|
|
|
|
if (p == NULL)
|
|
return;
|
|
st = (struct compat_state *)p;
|
|
free(st->name);
|
|
if (st->fp != NULL)
|
|
fclose(st->fp);
|
|
free(p);
|
|
}
|
|
|
|
|
|
static int
|
|
compat_setgrent(void *retval, void *mdata, va_list ap)
|
|
{
|
|
static const ns_src compatsrc[] = {
|
|
#ifdef YP
|
|
{ NSSRC_NIS, NS_SUCCESS },
|
|
#endif
|
|
{ NULL, 0 }
|
|
};
|
|
ns_dtab dtab[] = {
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_setgrent, NULL },
|
|
#endif
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_setgrent, NULL },
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
struct compat_state *st;
|
|
int rv, stayopen;
|
|
|
|
#define set_setent(x, y) do { \
|
|
int i; \
|
|
for (i = 0; i < (int)(nitems(x) - 1); i++) \
|
|
x[i].mdata = (void *)y; \
|
|
} while (0)
|
|
|
|
rv = compat_getstate(&st);
|
|
if (rv != 0)
|
|
return (NS_UNAVAIL);
|
|
switch ((enum constants)mdata) {
|
|
case SETGRENT:
|
|
stayopen = va_arg(ap, int);
|
|
if (st->fp != NULL)
|
|
rewind(st->fp);
|
|
else if (stayopen)
|
|
st->fp = fopen(_PATH_GROUP, "re");
|
|
set_setent(dtab, mdata);
|
|
(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
|
|
compatsrc, 0);
|
|
break;
|
|
case ENDGRENT:
|
|
if (st->fp != NULL) {
|
|
fclose(st->fp);
|
|
st->fp = NULL;
|
|
}
|
|
set_setent(dtab, mdata);
|
|
(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
|
|
compatsrc, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
st->compat = COMPAT_MODE_OFF;
|
|
free(st->name);
|
|
st->name = NULL;
|
|
return (NS_UNAVAIL);
|
|
#undef set_setent
|
|
}
|
|
|
|
|
|
static int
|
|
compat_group(void *retval, void *mdata, va_list ap)
|
|
{
|
|
static const ns_src compatsrc[] = {
|
|
#ifdef YP
|
|
{ NSSRC_NIS, NS_SUCCESS },
|
|
#endif
|
|
{ NULL, 0 }
|
|
};
|
|
ns_dtab dtab[] = {
|
|
#ifdef YP
|
|
{ NSSRC_NIS, nis_group, NULL },
|
|
#endif
|
|
#ifdef HESIOD
|
|
{ NSSRC_DNS, dns_group, NULL },
|
|
#endif
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
struct compat_state *st;
|
|
enum nss_lookup_type how;
|
|
const char *name, *line;
|
|
struct group *grp;
|
|
gid_t gid;
|
|
char *buffer, *p;
|
|
void *discard;
|
|
size_t bufsize, linesize;
|
|
off_t pos;
|
|
int rv, stayopen, *errnop;
|
|
|
|
#define set_lookup_type(x, y) do { \
|
|
int i; \
|
|
for (i = 0; i < (int)(nitems(x) - 1); i++) \
|
|
x[i].mdata = (void *)y; \
|
|
} while (0)
|
|
|
|
name = NULL;
|
|
gid = (gid_t)-1;
|
|
how = (enum nss_lookup_type)mdata;
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
name = va_arg(ap, const char *);
|
|
break;
|
|
case nss_lt_id:
|
|
gid = va_arg(ap, gid_t);
|
|
break;
|
|
case nss_lt_all:
|
|
break;
|
|
default:
|
|
return (NS_NOTFOUND);
|
|
}
|
|
grp = va_arg(ap, struct group *);
|
|
buffer = va_arg(ap, char *);
|
|
bufsize = va_arg(ap, size_t);
|
|
errnop = va_arg(ap, int *);
|
|
*errnop = compat_getstate(&st);
|
|
if (*errnop != 0)
|
|
return (NS_UNAVAIL);
|
|
if (st->fp == NULL &&
|
|
((st->fp = fopen(_PATH_GROUP, "re")) == NULL)) {
|
|
*errnop = errno;
|
|
rv = NS_UNAVAIL;
|
|
goto fin;
|
|
}
|
|
if (how == nss_lt_all)
|
|
stayopen = 1;
|
|
else {
|
|
rewind(st->fp);
|
|
stayopen = st->stayopen;
|
|
}
|
|
docompat:
|
|
switch (st->compat) {
|
|
case COMPAT_MODE_ALL:
|
|
set_lookup_type(dtab, how);
|
|
switch (how) {
|
|
case nss_lt_all:
|
|
rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
|
|
"getgrent_r", compatsrc, grp, buffer, bufsize,
|
|
errnop);
|
|
break;
|
|
case nss_lt_id:
|
|
rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
|
|
"getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
|
|
errnop);
|
|
break;
|
|
case nss_lt_name:
|
|
rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
|
|
"getgrnam_r", compatsrc, name, grp, buffer,
|
|
bufsize, errnop);
|
|
break;
|
|
}
|
|
if (rv & NS_TERMINATE)
|
|
goto fin;
|
|
st->compat = COMPAT_MODE_OFF;
|
|
break;
|
|
case COMPAT_MODE_NAME:
|
|
set_lookup_type(dtab, nss_lt_name);
|
|
rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
|
|
"getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
|
|
errnop);
|
|
switch (rv) {
|
|
case NS_SUCCESS:
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
if (strcmp(name, grp->gr_name) != 0)
|
|
rv = NS_NOTFOUND;
|
|
break;
|
|
case nss_lt_id:
|
|
if (gid != grp->gr_gid)
|
|
rv = NS_NOTFOUND;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case NS_RETURN:
|
|
goto fin;
|
|
default:
|
|
break;
|
|
}
|
|
free(st->name);
|
|
st->name = NULL;
|
|
st->compat = COMPAT_MODE_OFF;
|
|
if (rv == NS_SUCCESS)
|
|
goto fin;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
rv = NS_NOTFOUND;
|
|
pos = ftello(st->fp);
|
|
while ((line = fgetln(st->fp, &linesize)) != NULL) {
|
|
if (line[linesize-1] == '\n')
|
|
linesize--;
|
|
if (linesize > 2 && line[0] == '+') {
|
|
p = memchr(&line[1], ':', linesize);
|
|
if (p == NULL || p == &line[1])
|
|
st->compat = COMPAT_MODE_ALL;
|
|
else {
|
|
st->name = malloc(p - line);
|
|
if (st->name == NULL) {
|
|
syslog(LOG_ERR,
|
|
"getgrent memory allocation failure");
|
|
*errnop = ENOMEM;
|
|
rv = NS_UNAVAIL;
|
|
break;
|
|
}
|
|
memcpy(st->name, &line[1], p - line - 1);
|
|
st->name[p - line - 1] = '\0';
|
|
st->compat = COMPAT_MODE_NAME;
|
|
}
|
|
goto docompat;
|
|
}
|
|
rv = __gr_match_entry(line, linesize, how, name, gid);
|
|
if (rv != NS_SUCCESS)
|
|
continue;
|
|
/* We need room at least for the line, a string NUL
|
|
* terminator, alignment padding, and one (char *)
|
|
* pointer for the member list terminator.
|
|
*/
|
|
if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
|
|
*errnop = ERANGE;
|
|
rv = NS_RETURN;
|
|
break;
|
|
}
|
|
memcpy(buffer, line, linesize);
|
|
buffer[linesize] = '\0';
|
|
rv = __gr_parse_entry(buffer, linesize, grp,
|
|
&buffer[linesize + 1], bufsize - linesize - 1, errnop);
|
|
if (rv & NS_TERMINATE)
|
|
break;
|
|
pos = ftello(st->fp);
|
|
}
|
|
fin:
|
|
if (st->fp != NULL && !stayopen) {
|
|
fclose(st->fp);
|
|
st->fp = NULL;
|
|
}
|
|
if (rv == NS_SUCCESS && retval != NULL)
|
|
*(struct group **)retval = grp;
|
|
else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
|
|
fseeko(st->fp, pos, SEEK_SET);
|
|
return (rv);
|
|
#undef set_lookup_type
|
|
}
|
|
|
|
|
|
/*
|
|
* common group line matching and parsing
|
|
*/
|
|
int
|
|
__gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
|
|
const char *name, gid_t gid)
|
|
{
|
|
size_t namesize;
|
|
const char *p, *eol;
|
|
char *q;
|
|
unsigned long n;
|
|
int i, needed;
|
|
|
|
if (linesize == 0 || is_comment_line(line, linesize))
|
|
return (NS_NOTFOUND);
|
|
switch (how) {
|
|
case nss_lt_name: needed = 1; break;
|
|
case nss_lt_id: needed = 2; break;
|
|
default: needed = 2; break;
|
|
}
|
|
eol = &line[linesize];
|
|
for (p = line, i = 0; i < needed && p < eol; p++)
|
|
if (*p == ':')
|
|
i++;
|
|
if (i < needed)
|
|
return (NS_NOTFOUND);
|
|
switch (how) {
|
|
case nss_lt_name:
|
|
namesize = strlen(name);
|
|
if (namesize + 1 == (size_t)(p - line) &&
|
|
memcmp(line, name, namesize) == 0)
|
|
return (NS_SUCCESS);
|
|
break;
|
|
case nss_lt_id:
|
|
n = strtoul(p, &q, 10);
|
|
if (q < eol && *q == ':' && gid == (gid_t)n)
|
|
return (NS_SUCCESS);
|
|
break;
|
|
case nss_lt_all:
|
|
return (NS_SUCCESS);
|
|
default:
|
|
break;
|
|
}
|
|
return (NS_NOTFOUND);
|
|
}
|
|
|
|
|
|
int
|
|
__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
|
|
size_t membufsize, int *errnop)
|
|
{
|
|
char *s_gid, *s_mem, *p, **members;
|
|
unsigned long n;
|
|
int maxmembers;
|
|
|
|
memset(grp, 0, sizeof(*grp));
|
|
members = (char **)_ALIGN(membuf);
|
|
membufsize -= (char *)members - membuf;
|
|
maxmembers = membufsize / sizeof(*members);
|
|
if (maxmembers <= 0 ||
|
|
(grp->gr_name = strsep(&line, ":")) == NULL ||
|
|
grp->gr_name[0] == '\0' ||
|
|
(grp->gr_passwd = strsep(&line, ":")) == NULL ||
|
|
(s_gid = strsep(&line, ":")) == NULL ||
|
|
s_gid[0] == '\0')
|
|
return (NS_NOTFOUND);
|
|
s_mem = line;
|
|
n = strtoul(s_gid, &s_gid, 10);
|
|
if (s_gid[0] != '\0')
|
|
return (NS_NOTFOUND);
|
|
grp->gr_gid = (gid_t)n;
|
|
grp->gr_mem = members;
|
|
while (maxmembers > 1 && s_mem != NULL) {
|
|
p = strsep(&s_mem, ",");
|
|
if (p != NULL && *p != '\0') {
|
|
*members++ = p;
|
|
maxmembers--;
|
|
}
|
|
}
|
|
*members = NULL;
|
|
if (s_mem == NULL)
|
|
return (NS_SUCCESS);
|
|
else {
|
|
*errnop = ERANGE;
|
|
return (NS_RETURN);
|
|
}
|
|
}
|
|
|
|
|