diff options
author | nectar <nectar@FreeBSD.org> | 2003-04-17 14:15:26 +0000 |
---|---|---|
committer | nectar <nectar@FreeBSD.org> | 2003-04-17 14:15:26 +0000 |
commit | cd021cdb21c56ef20cd0d905c99c29cf24bd9773 (patch) | |
tree | 194a658195371c8a28e5f3ecbd1d1b1e2dfe5588 /lib/libc/gen/getgrent.c | |
parent | 1b1f6bb4f50d42bbbb1291be0c60741c12f8201a (diff) | |
download | FreeBSD-src-cd021cdb21c56ef20cd0d905c99c29cf24bd9773.zip FreeBSD-src-cd021cdb21c56ef20cd0d905c99c29cf24bd9773.tar.gz |
= Implement thread-safe versions of the getpwent(3) and getgrent(3)
family of functions using the new nsdispatch(3) core. Remove
arbitrary size limits when using the thread-safe versions.
= Re-implement the traditional getpwent(3)/getgrent(3) functions on
top of the thread-safe versions.
= Update the on-disk format of the hashed version of the passwd(5)
databases to allow for versioned entries. The legacy version is
`3'. (Don't ask.)
= Add support for version `4' entries in the passwd(5) database.
Entries in this format are identical to version 3 entries except
that all integers are stored as 32-bit integers in network byte
order (big endian).
= pwd_mkdb is updated to generate both version 3 and version 4
entries.
Sponsored by: DARPA, Network Associates Laboratories
Diffstat (limited to 'lib/libc/gen/getgrent.c')
-rw-r--r-- | lib/libc/gen/getgrent.c | 1528 |
1 files changed, 971 insertions, 557 deletions
diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c index a073946..86ba544 100644 --- a/lib/libc/gen/getgrent.c +++ b/lib/libc/gen/getgrent.c @@ -1,7 +1,12 @@ -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved. +/*- + * 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 @@ -11,18 +16,11 @@ * 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 University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * 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 REGENTS OR CONTRIBUTORS BE LIABLE + * 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) @@ -30,679 +28,1095 @@ * 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. + * */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; -#endif /* LIBC_SCCS and not lint */ -/* $NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry Exp $ */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/types.h> - +#include "namespace.h" +#include <sys/param.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#endif +#include <ctype.h> #include <errno.h> +#ifdef HESIOD +#include <hesiod.h> +#endif #include <grp.h> -#include <limits.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 HESIOD -#include <hesiod.h> -#include <arpa/nameser.h> -#endif -#ifdef YP -#include <rpc/rpc.h> -#include <rpcsvc/yp_prot.h> -#include <rpcsvc/ypclnt.h> -#endif - -#if defined(YP) || defined(HESIOD) -#define _GROUP_COMPAT -#endif -static FILE *_gr_fp; -static struct group _gr_group; -static int _gr_stayopen; -static int _gr_filesdone; +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 void grcleanup(void); -static int grscan(int, gid_t, const char *); -static char *getline(void); -static int copyline(const char*); -static int matchline(int, gid_t, const char *); -static int start_gr(void); +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; +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); -/* initial size for malloc and increase steps for realloc */ -#define MAXGRP 64 -#define MAXLINELENGTH 256 #ifdef HESIOD -#if MAXLINELENGTH < NS_MAXLABEL + 1 -#error "MAXLINELENGTH must be at least NS_MAXLABEL + 1" -#endif +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 -static char **members; /* list of group members */ -static int maxgrp; /* current length of **members */ -static char *line; /* buffer for group line */ -static int maxlinelength; /* current length of *line */ - -/* - * Lines longer than MAXLINELENGTHLIMIT will be counted as an error. - * <= 0 disable check for maximum line length - * 256K is enough for 64,000 uids - */ -#define MAXLINELENGTHLIMIT (256 * 1024) #ifdef YP -static char *__ypcurrent, *__ypdomain; -static int __ypcurrentlen; -static int _gr_ypdone; +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); + + +/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ +int +setgrent(void) +{ + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_setgrent, (void *)SETGRENT }, #ifdef HESIOD -static int _gr_hesnum; + { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, #endif - -#ifdef _GROUP_COMPAT -enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME }; -static enum _grmode __grmode; +#ifdef YP + { NSSRC_NIS, nis_setgrent, (void *)SETGRENT }, #endif + { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT }, + { NULL, NULL, NULL } + }; + (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0); + return (1); +} -struct group * -getgrent() + +int +setgroupent(int stayopen) { - if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL)) - return (NULL); - return &_gr_group; + static const ns_dtab 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 }, + { NULL, NULL, NULL } + }; + (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, + stayopen); + return (1); } -struct group * -getgrnam(name) - const char *name; + +void +endgrent(void) { - int rval; - - if (!start_gr()) - return NULL; - rval = grscan(1, 0, name); - if (!_gr_stayopen) - endgrent(); - return (rval) ? &_gr_group : NULL; + static const ns_dtab 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 }, + { NULL, NULL, NULL } + }; + (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc); } -struct group * -getgrgid(gid) - gid_t gid; + +int +getgrent_r(struct group *grp, char *buffer, size_t bufsize, + struct group **result) { - int rval; - - if (!start_gr()) - return NULL; - rval = grscan(1, gid, NULL); - if (!_gr_stayopen) - endgrent(); - return (rval) ? &_gr_group : NULL; + static const ns_dtab 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 }, + { NULL, NULL, NULL } + }; + int rv, ret_errno; + + ret_errno = 0; + *result = NULL; + rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc, + grp, buffer, bufsize, &ret_errno); + if (rv == NS_SUCCESS) + return (0); + else + return (ret_errno); } -void -grcleanup() + +int +getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, + struct group **result) { - _gr_filesdone = 0; + 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 - if (__ypcurrent) - free(__ypcurrent); - __ypcurrent = NULL; - _gr_ypdone = 0; + { NSSRC_NIS, nis_group, (void *)nss_lt_name }, #endif + { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, + { 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) +{ + static const ns_dtab dtab[] = { + { NSSRC_FILES, files_group, (void *)nss_lt_id }, #ifdef HESIOD - _gr_hesnum = 0; + { NSSRC_DNS, dns_group, (void *)nss_lt_id }, #endif -#ifdef _GROUP_COMPAT - __grmode = GRMODE_NONE; +#ifdef YP + { NSSRC_NIS, nis_group, (void *)nss_lt_id }, #endif + { NSSRC_COMPAT, compat_group, (void *)nss_lt_id }, + { 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); } -static int -start_gr() + +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) { - grcleanup(); - if (maxlinelength == 0) { - if ((line = (char *)malloc(MAXLINELENGTH)) == NULL) - return 0; - maxlinelength = MAXLINELENGTH; - } - if (maxgrp == 0) { - if ((members = (char **) malloc(sizeof(char**) * - MAXGRP)) == NULL) - return 0; - maxgrp = MAXGRP; + 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; } - if (_gr_fp) { - rewind(_gr_fp); - return 1; - } - return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0; + 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; + return (NULL); + } + grp_storage_size <<= 1; + grp_storage = malloc(grp_storage_size); + if (grp_storage == NULL) + return (NULL); + } + } while (res == NULL && rv == ERANGE); + return (res); } -int -setgrent(void) + +static int +wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize, + struct group **res) { - return setgroupent(0); + return (getgrnam_r(key.name, grp, buffer, bufsize, res)); } -int -setgroupent(stayopen) - int stayopen; + +static int +wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize, + struct group **res) { - if (!start_gr()) - return 0; - _gr_stayopen = stayopen; - return 1; + return (getgrgid_r(key.gid, grp, buffer, bufsize, res)); } -void -endgrent() + +static int +wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer, + size_t bufsize, struct group **res) { - grcleanup(); - if (_gr_fp) { - (void)fclose(_gr_fp); - _gr_fp = NULL; - } + return (getgrent_r(grp, buffer, bufsize, res)); } -static int _local_grscan(void *, void *, va_list); +struct group * +getgrnam(const char *name) +{ + union key key; -/*ARGSUSED*/ -static int -_local_grscan(rv, cb_data, ap) - void *rv; - void *cb_data; - va_list ap; + key.name = name; + return (getgr(wrap_getgrnam_r, key)); +} + + +struct group * +getgrgid(gid_t gid) { - int search = va_arg(ap, int); - gid_t gid = va_arg(ap, gid_t); - const char *name = va_arg(ap, const char *); - - if (_gr_filesdone) - return NS_NOTFOUND; - for (;;) { - if (getline() == NULL) { - if (!search) - _gr_filesdone = 1; - return NS_NOTFOUND; - } - if (matchline(search, gid, name)) - return NS_SUCCESS; - } - /* NOTREACHED */ + 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)); } -#ifdef HESIOD -static int _dns_grscan(void *, void *, va_list); -/*ARGSUSED*/ static int -_dns_grscan(rv, cb_data, ap) - void *rv; - void *cb_data; - va_list ap; +is_comment_line(const char *s, size_t n) { - int search = va_arg(ap, int); - gid_t gid = va_arg(ap, gid_t); - const char *name = va_arg(ap, const char *); - - char **hp; - void *context; - int r; - - r = NS_UNAVAIL; - if (!search && _gr_hesnum == -1) - return NS_NOTFOUND; - if (hesiod_init(&context) == -1) - return (r); - - for (;;) { - if (search) { - if (name) - strlcpy(line, name, maxlinelength); - else - snprintf(line, maxlinelength, "%u", - (unsigned int)gid); - } else { - snprintf(line, maxlinelength, "group-%u", _gr_hesnum); - _gr_hesnum++; - } + const char *eom; - hp = hesiod_resolve(context, line, "group"); - if (hp == NULL) { - if (errno == ENOENT) { - if (!search) - _gr_hesnum = -1; - r = NS_NOTFOUND; - } - break; - } + eom = &s[n]; - /* only check first elem */ - r = copyline(hp[0]); - hesiod_free_list(context, hp); - if (r == 0) { - r = NS_UNAVAIL; + for (; s < eom; s++) + if (*s == '#' || !isspace((unsigned char)*s)) break; - } - if (matchline(search, gid, name)) { - r = NS_SUCCESS; - break; - } else if (search) { - r = NS_NOTFOUND; - break; - } - } - hesiod_end(context); - return (r); + return (*s == '#' || s == eom); } -#endif -#ifdef YP -static int _nis_grscan(void *, void *, va_list); -/*ARGSUSED*/ -static int -_nis_grscan(rv, cb_data, ap) - void *rv; - void *cb_data; - va_list ap; +/* + * files backend + */ +static void +files_endstate(void *p) { - int search = va_arg(ap, int); - gid_t gid = va_arg(ap, gid_t); - const char *name = va_arg(ap, const char *); - char *key, *data; - int keylen, datalen; - int r; + if (p == NULL) + return; + if (((struct files_state *)p)->fp != NULL) + fclose(((struct files_state *)p)->fp); + free(p); +} - if(__ypdomain == NULL) { - switch (yp_get_default_domain(&__ypdomain)) { - case 0: - break; - case YPERR_RESRC: - return NS_TRYAGAIN; - default: - return NS_UNAVAIL; + +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, "r"); + break; + case ENDGRENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; } + break; + default: + break; } + return (NS_UNAVAIL); +} - if (search) { /* specific group or gid */ - if (name) - strlcpy(line, name, maxlinelength); - else - snprintf(line, maxlinelength, "%u", (unsigned int)gid); - data = NULL; - r = yp_match(__ypdomain, - (name) ? "group.byname" : "group.bygid", - line, (int)strlen(line), &data, &datalen); - switch (r) { - case 0: + +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; + 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, "r")) == NULL)) { + *errnop = errno; + return (NS_UNAVAIL); + } + if (how == nss_lt_all) + stayopen = 1; + else { + rewind(st->fp); + stayopen = st->stayopen; + } + rv = NS_NOTFOUND; + 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; - case YPERR_KEY: - if (data) - free(data); - return NS_NOTFOUND; - default: - if (data) - free(data); - return NS_UNAVAIL; } - data[datalen] = '\0'; /* clear trailing \n */ - r = copyline(data); - free(data); - if (r == 0) - return NS_UNAVAIL; - if (matchline(search, gid, name)) - return NS_SUCCESS; - else - return NS_NOTFOUND; + 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; } - - /* ! search */ - if (_gr_ypdone) - return NS_NOTFOUND; - for (;;) { - data = NULL; - if(__ypcurrent) { - key = NULL; - r = yp_next(__ypdomain, "group.byname", - __ypcurrent, __ypcurrentlen, - &key, &keylen, &data, &datalen); - free(__ypcurrent); - switch (r) { - case 0: - break; - case YPERR_NOMORE: - __ypcurrent = NULL; - if (key) - free(key); - if (data) - free(data); - _gr_ypdone = 1; - return NS_NOTFOUND; - default: - if (key) - free(key); - if (data) - free(data); - return NS_UNAVAIL; - } - __ypcurrent = key; - __ypcurrentlen = keylen; - } else { - if (yp_first(__ypdomain, "group.byname", - &__ypcurrent, &__ypcurrentlen, - &data, &datalen)) { - if (data) - free(data); - return NS_UNAVAIL; - } - } - data[datalen] = '\0'; /* clear trailing \n */ - r = copyline(data); - free(data); - if (r == 0) - return NS_UNAVAIL; - if (matchline(search, gid, name)) - return NS_SUCCESS; +fin: + if (!stayopen && st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; } - /* NOTREACHED */ + if (rv == NS_SUCCESS && retval != NULL) + *(struct group **)retval = grp; + return (rv); } -#endif -#ifdef _GROUP_COMPAT + +#ifdef HESIOD /* - * log an error if "files" or "compat" is specified in group_compat database + * dns backend */ -static int _bad_grscan(void *, void *, va_list); +static void +dns_endstate(void *p) +{ + + free(p); +} + -/*ARGSUSED*/ static int -_bad_grscan(rv, cb_data, ap) - void *rv; - void *cb_data; - va_list ap; +dns_setgrent(void *retval, void *cb_data, va_list ap) { - static int warned; + struct dns_state *st; + int rv; + + rv = dns_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + st->counter = 0; + return (NS_UNAVAIL); +} + - if (!warned) { - syslog(LOG_ERR, - "nsswitch.conf group_compat database can't use '%s'", - (char *)cb_data); +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; } - warned = 1; - return NS_UNAVAIL; + 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 /* - * when a name lookup in compat mode is required, look it up in group_compat - * nsswitch database. only Hesiod and NIS is supported - it doesn't make - * sense to lookup compat names from 'files' or 'compat' + * nis backend */ - -static int __grscancompat(int, gid_t, const char *); - -static int -__grscancompat(search, gid, name) - int search; - gid_t gid; - const char *name; +static void +nis_endstate(void *p) { - static const ns_dtab dtab[] = { - NS_FILES_CB(_bad_grscan, "files") - NS_DNS_CB(_dns_grscan, NULL) - NS_NIS_CB(_nis_grscan, NULL) - NS_COMPAT_CB(_bad_grscan, "compat") - { 0 } - }; - static const ns_src defaultnis[] = { - { NSSRC_NIS, NS_SUCCESS }, - { 0 } - }; - return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", - defaultnis, search, gid, name)); + if (p == NULL) + return; + free(((struct nis_state *)p)->key); + free(p); } -#endif - -static int _compat_grscan(void *, void *, va_list); -/*ARGSUSED*/ static int -_compat_grscan(rv, cb_data, ap) - void *rv; - void *cb_data; - va_list ap; +nis_setgrent(void *retval, void *cb_data, va_list ap) { - int search = va_arg(ap, int); - gid_t gid = va_arg(ap, gid_t); - const char *name = va_arg(ap, const char *); + 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); +} -#ifdef _GROUP_COMPAT - static char *grname = NULL; -#endif - for (;;) { -#ifdef _GROUP_COMPAT - if(__grmode != GRMODE_NONE) { - int r; - - switch(__grmode) { - case GRMODE_FULL: - r = __grscancompat(search, gid, name); - if (r == NS_SUCCESS) - return r; - __grmode = GRMODE_NONE; - break; - case GRMODE_NAME: - if(grname == (char *)NULL) { - __grmode = GRMODE_NONE; - break; - } - r = __grscancompat(1, 0, grname); - free(grname); - grname = (char *)NULL; - if (r != NS_SUCCESS) - break; - if (!search) - return NS_SUCCESS; - if (name) { - if (! strcmp(_gr_group.gr_name, name)) - return NS_SUCCESS; - } else { - if (_gr_group.gr_gid == gid) - return NS_SUCCESS; - } - break; - case GRMODE_NONE: - abort(); +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; } - continue; } -#endif /* _GROUP_COMPAT */ + /* 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 *)) + 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 */ - if (getline() == NULL) - return NS_NOTFOUND; -#ifdef _GROUP_COMPAT - if (line[0] == '+') { - char *tptr, *bp; - switch(line[1]) { - case ':': - case '\0': - case '\n': - __grmode = GRMODE_FULL; - break; - default: - __grmode = GRMODE_NAME; - bp = line; - tptr = strsep(&bp, ":\n"); - grname = strdup(tptr + 1); - break; - } - continue; - } -#endif /* _GROUP_COMPAT */ - if (matchline(search, gid, name)) - return NS_SUCCESS; - } - /* NOTREACHED */ +/* + * 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 -grscan(search, gid, name) - int search; - gid_t gid; - const char *name; +compat_setgrent(void *retval, void *mdata, va_list ap) { - int r; - static const ns_dtab dtab[] = { - NS_FILES_CB(_local_grscan, NULL) - NS_DNS_CB(_dns_grscan, NULL) - NS_NIS_CB(_nis_grscan, NULL) - NS_COMPAT_CB(_compat_grscan, NULL) - { 0 } - }; - static const ns_src compatsrc[] = { - { NSSRC_COMPAT, NS_SUCCESS }, - { 0 } - }; - - r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, - search, gid, name); - return (r == NS_SUCCESS) ? 1 : 0; + struct compat_state *st; + int rv, stayopen; + + 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, "r"); + break; + case ENDGRENT: + if (st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + break; + default: + break; + } + st->compat = COMPAT_MODE_OFF; + free(st->name); + st->name = NULL; + return (NS_UNAVAIL); } + static int -matchline(search, gid, name) - int search; - gid_t gid; - const char *name; +compat_group(void *retval, void *mdata, va_list ap) { - unsigned long id; - char **m; - char *cp, *bp, *ep; - - if (line[0] == '+') - return 0; /* sanity check to prevent recursion */ - bp = line; - _gr_group.gr_name = strsep(&bp, ":\n"); - if (search && name && strcmp(_gr_group.gr_name, name)) - return 0; - _gr_group.gr_passwd = strsep(&bp, ":\n"); - if (!(cp = strsep(&bp, ":\n"))) - return 0; - id = strtoul(cp, &ep, 10); - if (*ep != '\0') - return 0; - _gr_group.gr_gid = (gid_t)id; - if (search && name == NULL && _gr_group.gr_gid != gid) - return 0; - cp = NULL; - if (bp == NULL) - return 0; - for (_gr_group.gr_mem = m = members;; bp++) { - if (m == &members[maxgrp - 1]) { - members = (char **) reallocf(members, sizeof(char **) * - (maxgrp + MAXGRP)); - if (members == NULL) - return 0; - _gr_group.gr_mem = members; - m = &members[maxgrp - 1]; - maxgrp += MAXGRP; + 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; + int rv, stayopen, *errnop; + +#define set_lookup_type(x, y) do { \ + int i; \ + \ + for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 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, "r")) == 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 (*bp == ',') { - if (cp) { - *bp = '\0'; - *m++ = cp; - cp = NULL; + 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; } - } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { - if (cp) { - *bp = '\0'; - *m++ = cp; + 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; + 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; - } else if (cp == NULL) - cp = bp; - } - *m = NULL; - return 1; + } +fin: + if (!stayopen && st->fp != NULL) { + fclose(st->fp); + st->fp = NULL; + } + if (rv == NS_SUCCESS && retval != NULL) + *(struct group **)retval = grp; + return (rv); +#undef set_lookup_type } -static char * -getline(void) + +/* + * 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) { - const char *cp; - - tryagain: - if (fgets(line, maxlinelength, _gr_fp) == NULL) - return NULL; - if (index(line, '\n') == NULL) { - do { - if (feof(_gr_fp)) - return NULL; - if (MAXLINELENGTHLIMIT > 0 && - maxlinelength >= MAXLINELENGTHLIMIT) - return NULL; - line = (char *)reallocf(line, maxlinelength + - MAXLINELENGTH); - if (line == NULL) - return NULL; - if (fgets(line + maxlinelength - 1, - MAXLINELENGTH + 1, _gr_fp) == NULL) - return NULL; - maxlinelength += MAXLINELENGTH; - } while (index(line + maxlinelength - MAXLINELENGTH - 1, - '\n') == NULL); + 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; } - - - /* - * Ignore comments: ^[ \t]*# - */ - for (cp = line; *cp != '\0'; cp++) - if (*cp != ' ' && *cp != '\t') - break; - if (*cp == '#' || *cp == '\0') - goto tryagain; - - if (cp != line) /* skip white space at beginning of line */ - bcopy(cp, line, strlen(cp)); - - return line; + 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); } -static int -copyline(const char *src) + +int +__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf, + size_t membufsize, int *errnop) { - size_t sz; - - sz = strlen(src); - if (sz > maxlinelength - 1) { - sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH; - if ((line = (char *) reallocf(line, sz)) == NULL) - return 0; - maxlinelength = sz; + char *s_gid, *s_mem, **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; + if (s_mem[0] == '\0') { + *members = NULL; + return (NS_SUCCESS); + } + while (maxmembers > 1 && s_mem != NULL) { + *members++ = strsep(&s_mem, ","); + maxmembers--; + } + *members = NULL; + if (s_mem == NULL) + return (NS_SUCCESS); + else { + *errnop = ERANGE; + return (NS_RETURN); } - strlcpy(line, src, maxlinelength); - return 1; } - |